mirror of
https://github.com/extrawurst/gitui.git
synced 2024-11-25 07:47:13 +03:00
drop multiple stashes (#855)
This commit is contained in:
parent
5c694bd696
commit
2b85b81a3e
@ -7,12 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
**drop multiple stashes**
|
||||||
|
|
||||||
|
![drop-multiple-stashes](assets/drop-multiple-stashes.gif)
|
||||||
|
|
||||||
**branch name validation**
|
**branch name validation**
|
||||||
|
|
||||||
![name-validation](assets/branch-validation.gif)
|
![name-validation](assets/branch-validation.gif)
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
- mark and drop multiple stashes ([#854](https://github.com/extrawurst/gitui/issues/854))
|
||||||
- check branch name validity while typing ([#559](https://github.com/extrawurst/gitui/issues/559))
|
- check branch name validity while typing ([#559](https://github.com/extrawurst/gitui/issues/559))
|
||||||
- support deleting remote branch [[@zcorniere](https://github.com/zcorniere)] ([#622](https://github.com/extrawurst/gitui/issues/622))
|
- support deleting remote branch [[@zcorniere](https://github.com/zcorniere)] ([#622](https://github.com/extrawurst/gitui/issues/622))
|
||||||
|
|
||||||
|
BIN
assets/drop-multiple-stashes.gif
Normal file
BIN
assets/drop-multiple-stashes.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
18
src/app.rs
18
src/app.rs
@ -4,12 +4,12 @@ use crate::{
|
|||||||
components::{
|
components::{
|
||||||
event_pump, BlameFileComponent, BranchListComponent,
|
event_pump, BlameFileComponent, BranchListComponent,
|
||||||
CommandBlocking, CommandInfo, CommitComponent, Component,
|
CommandBlocking, CommandInfo, CommitComponent, Component,
|
||||||
CreateBranchComponent, DrawableComponent,
|
ConfirmComponent, CreateBranchComponent, DrawableComponent,
|
||||||
ExternalEditorComponent, HelpComponent,
|
ExternalEditorComponent, HelpComponent,
|
||||||
InspectCommitComponent, MsgComponent, PullComponent,
|
InspectCommitComponent, MsgComponent, PullComponent,
|
||||||
PushComponent, PushTagsComponent, RenameBranchComponent,
|
PushComponent, PushTagsComponent, RenameBranchComponent,
|
||||||
ResetComponent, RevisionFilesPopup, StashMsgComponent,
|
RevisionFilesPopup, StashMsgComponent, TagCommitComponent,
|
||||||
TagCommitComponent, TagListComponent,
|
TagListComponent,
|
||||||
},
|
},
|
||||||
input::{Input, InputEvent, InputState},
|
input::{Input, InputEvent, InputState},
|
||||||
keys::{KeyConfig, SharedKeyConfig},
|
keys::{KeyConfig, SharedKeyConfig},
|
||||||
@ -42,7 +42,7 @@ pub struct App {
|
|||||||
do_quit: bool,
|
do_quit: bool,
|
||||||
help: HelpComponent,
|
help: HelpComponent,
|
||||||
msg: MsgComponent,
|
msg: MsgComponent,
|
||||||
reset: ResetComponent,
|
reset: ConfirmComponent,
|
||||||
commit: CommitComponent,
|
commit: CommitComponent,
|
||||||
blame_file_popup: BlameFileComponent,
|
blame_file_popup: BlameFileComponent,
|
||||||
stashmsg_popup: StashMsgComponent,
|
stashmsg_popup: StashMsgComponent,
|
||||||
@ -91,7 +91,7 @@ impl App {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
input,
|
input,
|
||||||
reset: ResetComponent::new(
|
reset: ConfirmComponent::new(
|
||||||
queue.clone(),
|
queue.clone(),
|
||||||
theme.clone(),
|
theme.clone(),
|
||||||
key_config.clone(),
|
key_config.clone(),
|
||||||
@ -682,9 +682,13 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::StashDrop(_) | Action::StashPop(_) => {
|
Action::StashDrop(_) | Action::StashPop(_) => {
|
||||||
if self.stashlist_tab.action_confirmed(&action) {
|
if let Err(e) = StashList::action_confirmed(&action) {
|
||||||
flags.insert(NeedsUpdate::ALL);
|
self.queue.push(InternalEvent::ShowErrorMsg(
|
||||||
|
e.to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flags.insert(NeedsUpdate::ALL);
|
||||||
}
|
}
|
||||||
Action::ResetHunk(path, hash) => {
|
Action::ResetHunk(path, hash) => {
|
||||||
sync::reset_hunk(CWD, &path, hash)?;
|
sync::reset_hunk(CWD, &path, hash)?;
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
Component, DrawableComponent, EventState, ScrollType,
|
Component, DrawableComponent, EventState, ScrollType,
|
||||||
},
|
},
|
||||||
keys::SharedKeyConfig,
|
keys::SharedKeyConfig,
|
||||||
strings,
|
strings::{self, symbol},
|
||||||
ui::calc_scroll_top,
|
ui::calc_scroll_top,
|
||||||
ui::style::{SharedTheme, Theme},
|
ui::style::{SharedTheme, Theme},
|
||||||
};
|
};
|
||||||
@ -120,6 +120,23 @@ impl CommitList {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn selected_entry_marked(&self) -> bool {
|
||||||
|
self.selected_entry()
|
||||||
|
.and_then(|e| self.is_marked(&e.id))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn marked_count(&self) -> usize {
|
||||||
|
self.marked.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
pub fn marked(&self) -> &[CommitId] {
|
||||||
|
&self.marked
|
||||||
|
}
|
||||||
|
|
||||||
pub fn copy_entry_hash(&self) -> Result<()> {
|
pub fn copy_entry_hash(&self) -> Result<()> {
|
||||||
if let Some(e) = self.items.iter().nth(
|
if let Some(e) = self.items.iter().nth(
|
||||||
self.selection.saturating_sub(self.items.index_offset()),
|
self.selection.saturating_sub(self.items.index_offset()),
|
||||||
@ -223,14 +240,18 @@ impl CommitList {
|
|||||||
ELEMENTS_PER_LINE + if marked.is_some() { 2 } else { 0 },
|
ELEMENTS_PER_LINE + if marked.is_some() { 2 } else { 0 },
|
||||||
);
|
);
|
||||||
|
|
||||||
let splitter_txt = Cow::from(" ");
|
let splitter_txt = Cow::from(symbol::EMPTY_SPACE);
|
||||||
let splitter =
|
let splitter =
|
||||||
Span::styled(splitter_txt, theme.text(true, selected));
|
Span::styled(splitter_txt, theme.text(true, selected));
|
||||||
|
|
||||||
// marker
|
// marker
|
||||||
if let Some(marked) = marked {
|
if let Some(marked) = marked {
|
||||||
txt.push(Span::styled(
|
txt.push(Span::styled(
|
||||||
Cow::from(if marked { "\u{2713}" } else { " " }),
|
Cow::from(if marked {
|
||||||
|
symbol::CHECKMARK
|
||||||
|
} else {
|
||||||
|
symbol::EMPTY_SPACE
|
||||||
|
}),
|
||||||
theme.log_marker(selected),
|
theme.log_marker(selected),
|
||||||
));
|
));
|
||||||
txt.push(splitter.clone());
|
txt.push(splitter.clone());
|
||||||
@ -433,6 +454,14 @@ impl Component for CommitList {
|
|||||||
self.selected_entry().is_some(),
|
self.selected_entry().is_some(),
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
out.push(CommandInfo::new(
|
||||||
|
strings::commands::commit_list_mark(
|
||||||
|
&self.key_config,
|
||||||
|
self.selected_entry_marked(),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
));
|
||||||
CommandBlocking::PassingOn
|
CommandBlocking::PassingOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ pub use pull::PullComponent;
|
|||||||
pub use push::PushComponent;
|
pub use push::PushComponent;
|
||||||
pub use push_tags::PushTagsComponent;
|
pub use push_tags::PushTagsComponent;
|
||||||
pub use rename_branch::RenameBranchComponent;
|
pub use rename_branch::RenameBranchComponent;
|
||||||
pub use reset::ResetComponent;
|
pub use reset::ConfirmComponent;
|
||||||
pub use revision_files::RevisionFilesComponent;
|
pub use revision_files::RevisionFilesComponent;
|
||||||
pub use revision_files_popup::RevisionFilesPopup;
|
pub use revision_files_popup::RevisionFilesPopup;
|
||||||
pub use stashmsg::StashMsgComponent;
|
pub use stashmsg::StashMsgComponent;
|
||||||
|
@ -16,7 +16,7 @@ use tui::{
|
|||||||
use ui::style::SharedTheme;
|
use ui::style::SharedTheme;
|
||||||
|
|
||||||
///
|
///
|
||||||
pub struct ResetComponent {
|
pub struct ConfirmComponent {
|
||||||
target: Option<Action>,
|
target: Option<Action>,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
@ -24,7 +24,7 @@ pub struct ResetComponent {
|
|||||||
key_config: SharedKeyConfig,
|
key_config: SharedKeyConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawableComponent for ResetComponent {
|
impl DrawableComponent for ConfirmComponent {
|
||||||
fn draw<B: Backend>(
|
fn draw<B: Backend>(
|
||||||
&self,
|
&self,
|
||||||
f: &mut Frame<B>,
|
f: &mut Frame<B>,
|
||||||
@ -50,7 +50,7 @@ impl DrawableComponent for ResetComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for ResetComponent {
|
impl Component for ConfirmComponent {
|
||||||
fn commands(
|
fn commands(
|
||||||
&self,
|
&self,
|
||||||
out: &mut Vec<CommandInfo>,
|
out: &mut Vec<CommandInfo>,
|
||||||
@ -101,7 +101,7 @@ impl Component for ResetComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResetComponent {
|
impl ConfirmComponent {
|
||||||
///
|
///
|
||||||
pub fn new(
|
pub fn new(
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
@ -139,11 +139,11 @@ impl ResetComponent {
|
|||||||
strings::confirm_title_reset(),
|
strings::confirm_title_reset(),
|
||||||
strings::confirm_msg_reset(),
|
strings::confirm_msg_reset(),
|
||||||
),
|
),
|
||||||
Action::StashDrop(_) => (
|
Action::StashDrop(ids) => (
|
||||||
strings::confirm_title_stashdrop(
|
strings::confirm_title_stashdrop(
|
||||||
&self.key_config,
|
&self.key_config,ids.len()>1
|
||||||
),
|
),
|
||||||
strings::confirm_msg_stashdrop(&self.key_config),
|
strings::confirm_msg_stashdrop(&self.key_config,ids),
|
||||||
),
|
),
|
||||||
Action::StashPop(_) => (
|
Action::StashPop(_) => (
|
||||||
strings::confirm_title_stashpop(&self.key_config),
|
strings::confirm_title_stashpop(&self.key_config),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::strings::symbol;
|
||||||
use crate::ui::Size;
|
use crate::ui::Size;
|
||||||
use crate::{
|
use crate::{
|
||||||
components::{
|
components::{
|
||||||
@ -169,7 +170,7 @@ impl TextInputComponent {
|
|||||||
let cursor_highlighting = {
|
let cursor_highlighting = {
|
||||||
let mut h = HashMap::with_capacity(2);
|
let mut h = HashMap::with_capacity(2);
|
||||||
h.insert("\n", "\u{21b5}\n\r");
|
h.insert("\n", "\u{21b5}\n\r");
|
||||||
h.insert(" ", "\u{00B7}");
|
h.insert(" ", symbol::WHITESPACE);
|
||||||
h
|
h
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -470,7 +471,10 @@ mod tests {
|
|||||||
get_style(&txt.lines[0].0[0]),
|
get_style(&txt.lines[0].0[0]),
|
||||||
Some(¬_underlined)
|
Some(¬_underlined)
|
||||||
);
|
);
|
||||||
assert_eq!(get_text(&txt.lines[0].0[1]), Some("\u{00B7}"));
|
assert_eq!(
|
||||||
|
get_text(&txt.lines[0].0[1]),
|
||||||
|
Some(symbol::WHITESPACE)
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_style(&txt.lines[0].0[1]),
|
get_style(&txt.lines[0].0[1]),
|
||||||
Some(&underlined_whitespace)
|
Some(&underlined_whitespace)
|
||||||
|
@ -15,7 +15,7 @@ use std::{
|
|||||||
rc::Rc,
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::args::get_app_config_path;
|
use crate::{args::get_app_config_path, strings::symbol};
|
||||||
|
|
||||||
pub type SharedKeyConfig = Rc<KeyConfig>;
|
pub type SharedKeyConfig = Rc<KeyConfig>;
|
||||||
|
|
||||||
@ -248,6 +248,7 @@ impl KeyConfig {
|
|||||||
self.get_key_symbol(ev.code)
|
self.get_key_symbol(ev.code)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
KeyCode::Char(' ') => String::from(symbol::SPACE),
|
||||||
KeyCode::Char(c) => {
|
KeyCode::Char(c) => {
|
||||||
format!(
|
format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
|
@ -30,7 +30,7 @@ pub enum Action {
|
|||||||
Reset(ResetItem),
|
Reset(ResetItem),
|
||||||
ResetHunk(String, u64),
|
ResetHunk(String, u64),
|
||||||
ResetLines(String, Vec<DiffLinePosition>),
|
ResetLines(String, Vec<DiffLinePosition>),
|
||||||
StashDrop(CommitId),
|
StashDrop(Vec<CommitId>),
|
||||||
StashPop(CommitId),
|
StashPop(CommitId),
|
||||||
DeleteBranch(String, bool),
|
DeleteBranch(String, bool),
|
||||||
DeleteTag(String),
|
DeleteTag(String),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use asyncgit::sync::CommitId;
|
||||||
|
|
||||||
use crate::keys::SharedKeyConfig;
|
use crate::keys::SharedKeyConfig;
|
||||||
|
|
||||||
pub mod order {
|
pub mod order {
|
||||||
@ -20,6 +22,13 @@ pub static PUSH_TAGS_STATES_FETCHING: &str = "fetching";
|
|||||||
pub static PUSH_TAGS_STATES_PUSHING: &str = "pushing";
|
pub static PUSH_TAGS_STATES_PUSHING: &str = "pushing";
|
||||||
pub static PUSH_TAGS_STATES_DONE: &str = "done";
|
pub static PUSH_TAGS_STATES_DONE: &str = "done";
|
||||||
|
|
||||||
|
pub mod symbol {
|
||||||
|
pub const WHITESPACE: &str = "\u{00B7}"; //·
|
||||||
|
pub const CHECKMARK: &str = "\u{2713}"; //✓
|
||||||
|
pub const SPACE: &str = "\u{02FD}"; //˽
|
||||||
|
pub const EMPTY_SPACE: &str = " ";
|
||||||
|
}
|
||||||
|
|
||||||
pub fn title_branches() -> String {
|
pub fn title_branches() -> String {
|
||||||
"Branches".to_string()
|
"Branches".to_string()
|
||||||
}
|
}
|
||||||
@ -103,8 +112,9 @@ pub fn confirm_title_reset() -> String {
|
|||||||
}
|
}
|
||||||
pub fn confirm_title_stashdrop(
|
pub fn confirm_title_stashdrop(
|
||||||
_key_config: &SharedKeyConfig,
|
_key_config: &SharedKeyConfig,
|
||||||
|
multiple: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
"Drop".to_string()
|
format!("Drop Stash{}", if multiple { "es" } else { "" })
|
||||||
}
|
}
|
||||||
pub fn confirm_title_stashpop(
|
pub fn confirm_title_stashpop(
|
||||||
_key_config: &SharedKeyConfig,
|
_key_config: &SharedKeyConfig,
|
||||||
@ -151,8 +161,21 @@ pub fn confirm_msg_reset_lines(lines: usize) -> String {
|
|||||||
}
|
}
|
||||||
pub fn confirm_msg_stashdrop(
|
pub fn confirm_msg_stashdrop(
|
||||||
_key_config: &SharedKeyConfig,
|
_key_config: &SharedKeyConfig,
|
||||||
|
ids: &[CommitId],
|
||||||
) -> String {
|
) -> String {
|
||||||
"confirm stash drop?".to_string()
|
format!(
|
||||||
|
"Sure you want to drop following {}stash{}?\n\n{}",
|
||||||
|
if ids.len() > 1 {
|
||||||
|
format!("{} ", ids.len())
|
||||||
|
} else {
|
||||||
|
String::default()
|
||||||
|
},
|
||||||
|
if ids.len() > 1 { "es" } else { "" },
|
||||||
|
ids.iter()
|
||||||
|
.map(CommitId::get_short_string)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn confirm_msg_stashpop(_key_config: &SharedKeyConfig) -> String {
|
pub fn confirm_msg_stashpop(_key_config: &SharedKeyConfig) -> String {
|
||||||
"The stash will be applied and removed from the stash list. Confirm stash pop?"
|
"The stash will be applied and removed from the stash list. Confirm stash pop?"
|
||||||
@ -399,6 +422,20 @@ pub mod commands {
|
|||||||
CMD_GROUP_GENERAL,
|
CMD_GROUP_GENERAL,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
pub fn commit_list_mark(
|
||||||
|
key_config: &SharedKeyConfig,
|
||||||
|
marked: bool,
|
||||||
|
) -> CommandText {
|
||||||
|
CommandText::new(
|
||||||
|
format!(
|
||||||
|
"{} [{}]",
|
||||||
|
if marked { "Unmark" } else { "Mark" },
|
||||||
|
key_config.get_hint(key_config.log_mark_commit),
|
||||||
|
),
|
||||||
|
"mark multiple commits",
|
||||||
|
CMD_GROUP_GENERAL,
|
||||||
|
)
|
||||||
|
}
|
||||||
pub fn copy(key_config: &SharedKeyConfig) -> CommandText {
|
pub fn copy(key_config: &SharedKeyConfig) -> CommandText {
|
||||||
CommandText::new(
|
CommandText::new(
|
||||||
format!(
|
format!(
|
||||||
@ -828,10 +865,16 @@ pub mod commands {
|
|||||||
}
|
}
|
||||||
pub fn stashlist_drop(
|
pub fn stashlist_drop(
|
||||||
key_config: &SharedKeyConfig,
|
key_config: &SharedKeyConfig,
|
||||||
|
marked: usize,
|
||||||
) -> CommandText {
|
) -> CommandText {
|
||||||
CommandText::new(
|
CommandText::new(
|
||||||
format!(
|
format!(
|
||||||
"Drop [{}]",
|
"Drop{} [{}]",
|
||||||
|
if marked == 0 {
|
||||||
|
String::default()
|
||||||
|
} else {
|
||||||
|
format!(" {}", marked)
|
||||||
|
},
|
||||||
key_config.get_hint(key_config.stash_drop),
|
key_config.get_hint(key_config.stash_drop),
|
||||||
),
|
),
|
||||||
"drop selected stash",
|
"drop selected stash",
|
||||||
|
@ -71,9 +71,13 @@ impl StashList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn drop_stash(&mut self) {
|
fn drop_stash(&mut self) {
|
||||||
if let Some(e) = self.list.selected_entry() {
|
if self.list.marked_count() > 0 {
|
||||||
self.queue.push(InternalEvent::ConfirmAction(
|
self.queue.push(InternalEvent::ConfirmAction(
|
||||||
Action::StashDrop(e.id),
|
Action::StashDrop(self.list.marked().to_vec()),
|
||||||
|
));
|
||||||
|
} else if let Some(e) = self.list.selected_entry() {
|
||||||
|
self.queue.push(InternalEvent::ConfirmAction(
|
||||||
|
Action::StashDrop(vec![e.id]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,31 +97,27 @@ impl StashList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Called when a pending stash action has been confirmed
|
/// Called when a pending stash action has been confirmed
|
||||||
pub fn action_confirmed(&self, action: &Action) -> bool {
|
pub fn action_confirmed(action: &Action) -> Result<()> {
|
||||||
match *action {
|
match action {
|
||||||
Action::StashDrop(id) => Self::drop(id),
|
Action::StashDrop(ids) => Self::drop(ids)?,
|
||||||
Action::StashPop(id) => self.pop(id),
|
Action::StashPop(id) => Self::pop(*id)?,
|
||||||
_ => false,
|
_ => (),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drop(id: CommitId) -> bool {
|
fn drop(ids: &[CommitId]) -> Result<()> {
|
||||||
sync::stash_drop(CWD, id).is_ok()
|
for id in ids {
|
||||||
|
sync::stash_drop(CWD, *id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop(&self, id: CommitId) -> bool {
|
Ok(())
|
||||||
match sync::stash_pop(CWD, id) {
|
|
||||||
Ok(_) => {
|
|
||||||
self.queue.push(InternalEvent::TabSwitch);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
self.queue.push(InternalEvent::ShowErrorMsg(
|
|
||||||
format!("stash pop error:\n{}", e,),
|
|
||||||
));
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pop(id: CommitId) -> Result<()> {
|
||||||
|
sync::stash_pop(CWD, id)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +155,10 @@ impl Component for StashList {
|
|||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
out.push(CommandInfo::new(
|
out.push(CommandInfo::new(
|
||||||
strings::commands::stashlist_drop(&self.key_config),
|
strings::commands::stashlist_drop(
|
||||||
|
&self.key_config,
|
||||||
|
self.list.marked_count(),
|
||||||
|
),
|
||||||
selection_valid,
|
selection_valid,
|
||||||
true,
|
true,
|
||||||
));
|
));
|
||||||
|
Loading…
Reference in New Issue
Block a user