mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-29 06:33:52 +03:00
When opening items via project panel, only focus them on double-click
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
61346f734d
commit
2e6cf2011d
@ -3898,7 +3898,7 @@ mod tests {
|
||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "main.rs"), cx)
|
||||
workspace.open_path((worktree_id, "main.rs"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -4146,7 +4146,7 @@ mod tests {
|
||||
let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(¶ms, cx));
|
||||
let editor_b = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "one.rs"), cx)
|
||||
workspace.open_path((worktree_id, "one.rs"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -4898,7 +4898,7 @@ mod tests {
|
||||
let pane_a = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
let editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), cx)
|
||||
workspace.open_path((worktree_id, "1.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -4906,7 +4906,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let editor_a2 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "2.txt"), cx)
|
||||
workspace.open_path((worktree_id, "2.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -4917,7 +4917,7 @@ mod tests {
|
||||
let workspace_b = client_b.build_workspace(&project_b, cx_b);
|
||||
let editor_b1 = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), cx)
|
||||
workspace.open_path((worktree_id, "1.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -5110,7 +5110,7 @@ mod tests {
|
||||
let pane_a1 = workspace_a.read_with(cx_a, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), cx)
|
||||
workspace.open_path((worktree_id, "1.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -5122,7 +5122,7 @@ mod tests {
|
||||
let pane_b1 = workspace_b.read_with(cx_b, |workspace, _| workspace.active_pane().clone());
|
||||
let _editor_b1 = workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "2.txt"), cx)
|
||||
workspace.open_path((worktree_id, "2.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -5157,7 +5157,7 @@ mod tests {
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.activate_next_pane(cx);
|
||||
assert_eq!(*workspace.active_pane(), pane_a1);
|
||||
workspace.open_path((worktree_id, "3.txt"), cx)
|
||||
workspace.open_path((worktree_id, "3.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@ -5165,7 +5165,7 @@ mod tests {
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.activate_next_pane(cx);
|
||||
assert_eq!(*workspace.active_pane(), pane_b1);
|
||||
workspace.open_path((worktree_id, "4.txt"), cx)
|
||||
workspace.open_path((worktree_id, "4.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@ -5254,7 +5254,7 @@ mod tests {
|
||||
let workspace_a = client_a.build_workspace(&project_a, cx_a);
|
||||
let _editor_a1 = workspace_a
|
||||
.update(cx_a, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "1.txt"), cx)
|
||||
workspace.open_path((worktree_id, "1.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
@ -5367,7 +5367,7 @@ mod tests {
|
||||
// When client B activates a different item in the original pane, it automatically stops following client A.
|
||||
workspace_b
|
||||
.update(cx_b, |workspace, cx| {
|
||||
workspace.open_path((worktree_id, "2.txt"), cx)
|
||||
workspace.open_path((worktree_id, "2.txt"), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -102,7 +102,7 @@ impl FileFinder {
|
||||
match event {
|
||||
Event::Selected(project_path) => {
|
||||
workspace
|
||||
.open_path(project_path.clone(), cx)
|
||||
.open_path(project_path.clone(), true, cx)
|
||||
.detach_and_log_err(cx);
|
||||
workspace.dismiss_modal(cx);
|
||||
}
|
||||
|
@ -103,7 +103,10 @@ pub fn init(cx: &mut MutableAppContext) {
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
OpenedEntry(ProjectEntryId),
|
||||
OpenedEntry {
|
||||
entry_id: ProjectEntryId,
|
||||
focus_opened_item: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl ProjectPanel {
|
||||
@ -157,19 +160,29 @@ impl ProjectPanel {
|
||||
this.update_visible_entries(None, cx);
|
||||
this
|
||||
});
|
||||
cx.subscribe(&project_panel, move |workspace, _, event, cx| match event {
|
||||
&Event::OpenedEntry(entry_id) => {
|
||||
if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
|
||||
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
|
||||
workspace
|
||||
.open_path(
|
||||
ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: entry.path.clone(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
cx.subscribe(&project_panel, {
|
||||
let project_panel = project_panel.clone();
|
||||
move |workspace, _, event, cx| match event {
|
||||
&Event::OpenedEntry {
|
||||
entry_id,
|
||||
focus_opened_item,
|
||||
} => {
|
||||
if let Some(worktree) = project.read(cx).worktree_for_entry(entry_id, cx) {
|
||||
if let Some(entry) = worktree.read(cx).entry_for_id(entry_id) {
|
||||
workspace
|
||||
.open_path(
|
||||
ProjectPath {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: entry.path.clone(),
|
||||
},
|
||||
focus_opened_item,
|
||||
cx,
|
||||
)
|
||||
.detach_and_log_err(cx);
|
||||
if !focus_opened_item {
|
||||
cx.focus(&project_panel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,7 +211,10 @@ impl ProjectPanel {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let event = Event::OpenedEntry(entry.id);
|
||||
let event = Event::OpenedEntry {
|
||||
entry_id: entry.id,
|
||||
focus_opened_item: true,
|
||||
};
|
||||
cx.emit(event);
|
||||
}
|
||||
}
|
||||
@ -342,7 +358,10 @@ impl ProjectPanel {
|
||||
}
|
||||
|
||||
fn open_entry(&mut self, action: &Open, cx: &mut ViewContext<Self>) {
|
||||
cx.emit(Event::OpenedEntry(action.entry_id));
|
||||
cx.emit(Event::OpenedEntry {
|
||||
entry_id: action.entry_id,
|
||||
focus_opened_item: action.change_focus,
|
||||
});
|
||||
}
|
||||
|
||||
fn add_file(&mut self, _: &AddFile, cx: &mut ViewContext<Self>) {
|
||||
|
@ -50,7 +50,7 @@ impl<'a> VimTestContext<'a> {
|
||||
|
||||
let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
|
||||
let item = workspace
|
||||
.update(cx, |workspace, cx| workspace.open_path(file, cx))
|
||||
.update(cx, |workspace, cx| workspace.open_path(file, true, cx))
|
||||
.await
|
||||
.expect("Could not open test file");
|
||||
|
||||
|
@ -59,7 +59,7 @@ const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
|
||||
|
||||
pub fn init(cx: &mut MutableAppContext) {
|
||||
cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| {
|
||||
pane.activate_item(action.0, true, cx);
|
||||
pane.activate_item(action.0, true, true, cx);
|
||||
});
|
||||
cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| {
|
||||
pane.activate_prev_item(cx);
|
||||
@ -213,7 +213,7 @@ impl Pane {
|
||||
{
|
||||
let prev_active_item_index = pane.active_item_index;
|
||||
pane.nav_history.borrow_mut().set_mode(mode);
|
||||
pane.activate_item(index, true, cx);
|
||||
pane.activate_item(index, true, true, cx);
|
||||
pane.nav_history
|
||||
.borrow_mut()
|
||||
.set_mode(NavigationMode::Normal);
|
||||
@ -257,6 +257,7 @@ impl Pane {
|
||||
workspace,
|
||||
pane.clone(),
|
||||
project_entry_id,
|
||||
true,
|
||||
cx,
|
||||
build_item,
|
||||
)
|
||||
@ -287,6 +288,7 @@ impl Pane {
|
||||
workspace: &mut Workspace,
|
||||
pane: ViewHandle<Pane>,
|
||||
project_entry_id: ProjectEntryId,
|
||||
focus_item: bool,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
build_item: impl FnOnce(&mut MutableAppContext) -> Box<dyn ItemHandle>,
|
||||
) -> Box<dyn ItemHandle> {
|
||||
@ -294,7 +296,7 @@ impl Pane {
|
||||
for (ix, item) in pane.items.iter().enumerate() {
|
||||
if item.project_entry_id(cx) == Some(project_entry_id) {
|
||||
let item = item.boxed_clone();
|
||||
pane.activate_item(ix, true, cx);
|
||||
pane.activate_item(ix, true, focus_item, cx);
|
||||
return Some(item);
|
||||
}
|
||||
}
|
||||
@ -304,7 +306,7 @@ impl Pane {
|
||||
existing_item
|
||||
} else {
|
||||
let item = build_item(cx);
|
||||
Self::add_item(workspace, pane, item.boxed_clone(), true, cx);
|
||||
Self::add_item(workspace, pane, item.boxed_clone(), true, focus_item, cx);
|
||||
item
|
||||
}
|
||||
}
|
||||
@ -313,12 +315,15 @@ impl Pane {
|
||||
workspace: &mut Workspace,
|
||||
pane: ViewHandle<Pane>,
|
||||
item: Box<dyn ItemHandle>,
|
||||
local: bool,
|
||||
activate_pane: bool,
|
||||
focus_item: bool,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
// Prevent adding the same item to the pane more than once.
|
||||
if let Some(item_ix) = pane.read(cx).items.iter().position(|i| i.id() == item.id()) {
|
||||
pane.update(cx, |pane, cx| pane.activate_item(item_ix, local, cx));
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.activate_item(item_ix, activate_pane, focus_item, cx)
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -327,7 +332,7 @@ impl Pane {
|
||||
pane.update(cx, |pane, cx| {
|
||||
let item_idx = cmp::min(pane.active_item_index + 1, pane.items.len());
|
||||
pane.items.insert(item_idx, item);
|
||||
pane.activate_item(item_idx, local, cx);
|
||||
pane.activate_item(item_idx, activate_pane, focus_item, cx);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
@ -378,7 +383,13 @@ impl Pane {
|
||||
self.items.iter().position(|i| i.id() == item.id())
|
||||
}
|
||||
|
||||
pub fn activate_item(&mut self, index: usize, local: bool, cx: &mut ViewContext<Self>) {
|
||||
pub fn activate_item(
|
||||
&mut self,
|
||||
index: usize,
|
||||
activate_pane: bool,
|
||||
focus_item: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
use NavigationMode::{GoingBack, GoingForward};
|
||||
if index < self.items.len() {
|
||||
let prev_active_item_ix = mem::replace(&mut self.active_item_index, index);
|
||||
@ -387,11 +398,15 @@ impl Pane {
|
||||
&& prev_active_item_ix < self.items.len())
|
||||
{
|
||||
self.items[prev_active_item_ix].deactivated(cx);
|
||||
cx.emit(Event::ActivateItem { local });
|
||||
cx.emit(Event::ActivateItem {
|
||||
local: activate_pane,
|
||||
});
|
||||
}
|
||||
self.update_toolbar(cx);
|
||||
if local {
|
||||
if focus_item {
|
||||
self.focus_active_item(cx);
|
||||
}
|
||||
if activate_pane {
|
||||
self.activate(cx);
|
||||
}
|
||||
self.autoscroll = true;
|
||||
@ -406,7 +421,7 @@ impl Pane {
|
||||
} else if self.items.len() > 0 {
|
||||
index = self.items.len() - 1;
|
||||
}
|
||||
self.activate_item(index, true, cx);
|
||||
self.activate_item(index, true, true, cx);
|
||||
}
|
||||
|
||||
pub fn activate_next_item(&mut self, cx: &mut ViewContext<Self>) {
|
||||
@ -416,7 +431,7 @@ impl Pane {
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
self.activate_item(index, true, cx);
|
||||
self.activate_item(index, true, true, cx);
|
||||
}
|
||||
|
||||
fn close_active_item(
|
||||
@ -498,7 +513,7 @@ impl Pane {
|
||||
if is_last_item_for_entry {
|
||||
if cx.read(|cx| item.has_conflict(cx) && item.can_save(cx)) {
|
||||
let mut answer = pane.update(&mut cx, |pane, cx| {
|
||||
pane.activate_item(item_to_close_ix, true, cx);
|
||||
pane.activate_item(item_to_close_ix, true, true, cx);
|
||||
cx.prompt(
|
||||
PromptLevel::Warning,
|
||||
CONFLICT_MESSAGE,
|
||||
@ -518,7 +533,7 @@ impl Pane {
|
||||
} else if cx.read(|cx| item.is_dirty(cx)) {
|
||||
if cx.read(|cx| item.can_save(cx)) {
|
||||
let mut answer = pane.update(&mut cx, |pane, cx| {
|
||||
pane.activate_item(item_to_close_ix, true, cx);
|
||||
pane.activate_item(item_to_close_ix, true, true, cx);
|
||||
cx.prompt(
|
||||
PromptLevel::Warning,
|
||||
DIRTY_MESSAGE,
|
||||
@ -535,7 +550,7 @@ impl Pane {
|
||||
}
|
||||
} else if cx.read(|cx| item.can_save_as(cx)) {
|
||||
let mut answer = pane.update(&mut cx, |pane, cx| {
|
||||
pane.activate_item(item_to_close_ix, true, cx);
|
||||
pane.activate_item(item_to_close_ix, true, true, cx);
|
||||
cx.prompt(
|
||||
PromptLevel::Warning,
|
||||
DIRTY_MESSAGE,
|
||||
@ -949,7 +964,7 @@ mod tests {
|
||||
|
||||
let close_items = workspace.update(cx, |workspace, cx| {
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.activate_item(1, true, cx);
|
||||
pane.activate_item(1, true, true, cx);
|
||||
assert_eq!(pane.active_item().unwrap().id(), item2.id());
|
||||
});
|
||||
|
||||
|
@ -493,7 +493,7 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
|
||||
if T::should_activate_item_on_event(event) {
|
||||
pane.update(cx, |pane, cx| {
|
||||
if let Some(ix) = pane.index_for_item(&item) {
|
||||
pane.activate_item(ix, true, cx);
|
||||
pane.activate_item(ix, true, true, cx);
|
||||
pane.activate(cx);
|
||||
}
|
||||
});
|
||||
@ -898,7 +898,7 @@ impl Workspace {
|
||||
if fs.is_file(&abs_path).await {
|
||||
Some(
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.open_path(project_path, cx)
|
||||
this.open_path(project_path, true, cx)
|
||||
})
|
||||
.await,
|
||||
)
|
||||
@ -1099,12 +1099,13 @@ impl Workspace {
|
||||
|
||||
pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
|
||||
let pane = self.active_pane().clone();
|
||||
Pane::add_item(self, pane, item, true, cx);
|
||||
Pane::add_item(self, pane, item, true, true, cx);
|
||||
}
|
||||
|
||||
pub fn open_path(
|
||||
&mut self,
|
||||
path: impl Into<ProjectPath>,
|
||||
focus_item: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<Box<dyn ItemHandle>, Arc<anyhow::Error>>> {
|
||||
let pane = self.active_pane().downgrade();
|
||||
@ -1119,6 +1120,7 @@ impl Workspace {
|
||||
this,
|
||||
pane,
|
||||
project_entry_id,
|
||||
focus_item,
|
||||
cx,
|
||||
build_item,
|
||||
))
|
||||
@ -1187,7 +1189,7 @@ impl Workspace {
|
||||
});
|
||||
if let Some((pane, ix)) = result {
|
||||
self.activate_pane(pane.clone(), cx);
|
||||
pane.update(cx, |pane, cx| pane.activate_item(ix, true, cx));
|
||||
pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, cx));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@ -1277,7 +1279,7 @@ impl Workspace {
|
||||
self.activate_pane(new_pane.clone(), cx);
|
||||
if let Some(item) = pane.read(cx).active_item() {
|
||||
if let Some(clone) = item.clone_on_split(cx.as_mut()) {
|
||||
Pane::add_item(self, new_pane.clone(), clone, true, cx);
|
||||
Pane::add_item(self, new_pane.clone(), clone, true, true, cx);
|
||||
}
|
||||
}
|
||||
self.center.split(&pane, &new_pane, direction).unwrap();
|
||||
@ -1961,7 +1963,7 @@ impl Workspace {
|
||||
}
|
||||
|
||||
for (pane, item) in items_to_add {
|
||||
Pane::add_item(self, pane.clone(), item.boxed_clone(), false, cx);
|
||||
Pane::add_item(self, pane.clone(), item.boxed_clone(), false, false, cx);
|
||||
if pane == self.active_pane {
|
||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||
}
|
||||
|
@ -446,7 +446,7 @@ mod tests {
|
||||
|
||||
// Open the first entry
|
||||
let entry_1 = workspace
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), true, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.read(|cx| {
|
||||
@ -460,7 +460,7 @@ mod tests {
|
||||
|
||||
// Open the second entry
|
||||
workspace
|
||||
.update(cx, |w, cx| w.open_path(file2.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file2.clone(), true, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
cx.read(|cx| {
|
||||
@ -474,7 +474,7 @@ mod tests {
|
||||
|
||||
// Open the first entry again. The existing pane item is activated.
|
||||
let entry_1b = workspace
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), true, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(entry_1.id(), entry_1b.id());
|
||||
@ -492,7 +492,7 @@ mod tests {
|
||||
workspace
|
||||
.update(cx, |w, cx| {
|
||||
w.split_pane(w.active_pane().clone(), SplitDirection::Right, cx);
|
||||
w.open_path(file2.clone(), cx)
|
||||
w.open_path(file2.clone(), true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@ -511,8 +511,8 @@ mod tests {
|
||||
// Open the third entry twice concurrently. Only one pane item is added.
|
||||
let (t1, t2) = workspace.update(cx, |w, cx| {
|
||||
(
|
||||
w.open_path(file3.clone(), cx),
|
||||
w.open_path(file3.clone(), cx),
|
||||
w.open_path(file3.clone(), true, cx),
|
||||
w.open_path(file3.clone(), true, cx),
|
||||
)
|
||||
});
|
||||
t1.await.unwrap();
|
||||
@ -780,6 +780,7 @@ mod tests {
|
||||
worktree_id: worktree.read(cx).id(),
|
||||
path: Path::new("the-new-name.rs").into(),
|
||||
},
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
@ -875,7 +876,7 @@ mod tests {
|
||||
let pane_1 = cx.read(|cx| workspace.read(cx).active_pane().clone());
|
||||
|
||||
workspace
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), true, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -955,7 +956,7 @@ mod tests {
|
||||
let file3 = entries[2].clone();
|
||||
|
||||
let editor1 = workspace
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file1.clone(), true, cx))
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
@ -964,13 +965,13 @@ mod tests {
|
||||
editor.select_display_ranges(&[DisplayPoint::new(10, 0)..DisplayPoint::new(10, 0)], cx);
|
||||
});
|
||||
let editor2 = workspace
|
||||
.update(cx, |w, cx| w.open_path(file2.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file2.clone(), true, cx))
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
.unwrap();
|
||||
let editor3 = workspace
|
||||
.update(cx, |w, cx| w.open_path(file3.clone(), cx))
|
||||
.update(cx, |w, cx| w.open_path(file3.clone(), true, cx))
|
||||
.await
|
||||
.unwrap()
|
||||
.downcast::<Editor>()
|
||||
|
Loading…
Reference in New Issue
Block a user