2 Commits
Author SHA1 Message Date
reya 6efc00d2de refactor group meta toggle into separate show/hide actions 2026-10-01 11:22:04 +07:00
reya 2eba27a94c redesign activity rows 2026-10-01 11:00:41 +07:00
8 changed files with 143 additions and 64 deletions
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M21.25 12C21.25 17.1086 17.1086 21.25 12 21.25C9.44568 21.25 7.13318 20.2147 5.45926 18.5407C3.78534 16.8668 2.75 14.5543 2.75 12C2.75 6.89137 6.89137 2.75 12 2.75C14.5543 2.75 16.8668 3.78534 18.5407 5.45926C20.2147 7.13318 21.25 9.44568 21.25 12Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"/><path d="M18 6L6 18" stroke="currentColor" stroke-width="1.5" stroke-linecap="square"/> </svg>

After

Width:  |  Height:  |  Size: 493 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M14.75 3.75L18.25 3.75C19.3546 3.75 20.25 4.64543 20.25 5.75V18.25C20.25 19.3546 19.3546 20.25 18.25 20.25H14.75M15 12H3.75M15 12L11.5 15.5M15 12L11.5 8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>

After

Width:  |  Height:  |  Size: 335 B

+31 -27
View File
@@ -33,10 +33,18 @@ mod invite;
mod message;
mod metadata;
/// Ask the shell to show or hide the sidebar's information for a group.
/// Ask the shell to show the sidebar's information for a group.
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = nip29_ui, no_json)]
pub struct ToggleGroupMeta {
pub struct ShowGroupMeta {
pub relay: RelayUrl,
pub group: String,
}
/// Ask the shell to hide the sidebar's information for a group.
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = nip29_ui, no_json)]
pub struct HideGroupMeta {
pub relay: RelayUrl,
pub group: String,
}
@@ -870,15 +878,6 @@ impl GroupPanel {
supports_text && writable
});
let meta = self.group.upgrade().map(|group| {
let group = group.read(cx);
(
group.key().relay().clone(),
group.key().id().as_str().to_owned(),
)
});
v_flex()
.flex_shrink_0()
.w_full()
@@ -889,22 +888,6 @@ impl GroupPanel {
h_flex()
.gap_1()
.items_end()
.child(
Button::new("info")
.icon(IconName::Group)
.tooltip("Toggle group information in the sidebar")
.ghost()
.large()
.on_click(move |_event, window, cx| {
let Some((relay, group)) = meta.clone() else {
return;
};
window.dispatch_action(
Box::new(ToggleGroupMeta { relay, group }),
cx,
);
}),
)
.child(
Button::new("upload")
.icon(IconName::Upload)
@@ -1000,6 +983,27 @@ impl Panel for GroupPanel {
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
Vec::new()
}
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
let Some(group) = self.group.upgrade() else {
return;
};
let (relay, id) = {
let group = group.read(cx);
(
group.key().relay().clone(),
group.key().id().as_str().to_owned(),
)
};
if active {
window.dispatch_action(Box::new(ShowGroupMeta { relay, group: id }), cx);
} else {
window.dispatch_action(Box::new(HideGroupMeta { relay, group: id }), cx);
}
}
}
impl EventEmitter<PanelEvent> for GroupPanel {}
+57 -13
View File
@@ -13,6 +13,7 @@ use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::menu::{ContextMenu, PopupMenu, PopupMenuItem};
use ui::message::MessageRow;
use ui::{Icon, IconName, Sizable, StyledExt, h_flex};
use crate::GroupPanel;
@@ -134,20 +135,63 @@ pub(crate) fn render_activity(ix: usize, activity: &Activity, cx: &App) -> AnyEl
let subject = persons.read(cx).get(&activity.subject(), cx);
let hide_avatar = AppSettings::get_hide_avatar(cx);
MessageRow::new(ix)
.show_author(true)
.hide_avatar(hide_avatar)
.avatar(
Avatar::new(subject.avatar())
.seed(subject.avatar_seed())
.flex_shrink_0(),
h_flex()
.id(ix)
.w_full()
.gap_2()
.py_1()
.px_3()
.hover(|this| this.bg(cx.theme().surface_background))
.child(badge(activity, cx))
.when(!hide_avatar, |this| {
this.child(
Avatar::new(subject.avatar())
.seed(subject.avatar_seed())
.small()
.flex_shrink_0(),
)
})
.child(
div()
.min_w_0()
.truncate()
.text_sm()
.font_semibold()
.child(subject.name()),
)
.author(SharedString::from(format!(
"{} {}",
subject.name(),
action(activity)
)))
.timestamp(activity.created_at.to_human_time())
.child(
div()
.min_w_0()
.truncate()
.text_sm()
.text_color(cx.theme().text_placeholder)
.child(SharedString::from(action(activity))),
)
.child(
div()
.flex_shrink_0()
.ml_1()
.text_xs()
.text_color(cx.theme().text_placeholder)
.child(activity.created_at.to_ago()),
)
.into_any_element()
}
fn badge(activity: &Activity, cx: &App) -> AnyElement {
let (icon, color) = match activity.kind {
Kind::GroupJoinRequest => (IconName::Login, cx.theme().icon_accent),
Kind::GroupPutUser => (IconName::Login, cx.theme().icon_accent),
Kind::GroupLeaveRequest => (IconName::Logout, cx.theme().text_danger),
Kind::GroupRemoveUser => (IconName::Ban, cx.theme().text_danger),
_ => (IconName::Info, cx.theme().icon_muted),
};
h_flex()
.flex_shrink_0()
.justify_center()
.size_8()
.child(Icon::new(icon).small().text_color(color))
.into_any_element()
}
+6 -6
View File
@@ -48,7 +48,7 @@ pub trait Panel: EventEmitter<PanelEvent> + Render + Focusable {
/// This method will be called when the panel is active or inactive.
///
/// The last_active_panel and current_active_panel will be touched when the panel is active.
fn set_active(&self, _active: bool, _cx: &mut App) {}
fn set_active(&self, _active: bool, _window: &mut Window, _cx: &mut App) {}
/// Set zoomed state of the panel.
///
@@ -74,7 +74,7 @@ pub trait PanelView: 'static + Send + Sync {
fn closable(&self, cx: &App) -> bool;
fn zoomable(&self, cx: &App) -> bool;
fn visible(&self, cx: &App) -> bool;
fn set_active(&self, active: bool, cx: &mut App);
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App);
fn set_zoomed(&self, zoomed: bool, cx: &mut App);
fn popup_menu(&self, menu: PopupMenu, cx: &App) -> PopupMenu;
fn toolbar_buttons(&self, window: &Window, cx: &App) -> Vec<Button>;
@@ -103,9 +103,9 @@ impl<T: Panel> PanelView for Entity<T> {
self.read(cx).visible(cx)
}
fn set_active(&self, active: bool, cx: &mut App) {
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
self.update(cx, |this, cx| {
this.set_active(active, cx);
this.set_active(active, window, cx);
})
}
@@ -196,8 +196,8 @@ impl gpui_base::dock::PanelView for PanelHandle {
self.panel.visible(cx)
}
fn set_active(&self, active: bool, _: &mut Window, cx: &mut App) {
self.panel.set_active(active, cx);
fn set_active(&self, active: bool, window: &mut Window, cx: &mut App) {
self.panel.set_active(active, window, cx);
}
fn set_zoomed(&self, zoomed: bool, _: &mut Window, cx: &mut App) {
+4
View File
@@ -25,6 +25,7 @@ pub enum IconName {
ArrowDownCircle,
Boom,
Book,
Ban,
ChevronDown,
CaretDown,
CaretRight,
@@ -50,6 +51,7 @@ pub enum IconName {
Link,
Loader,
Lock,
Login,
Logout,
Message,
Moon,
@@ -106,6 +108,7 @@ impl IconNamed for IconName {
Self::ArrowDownCircle => "icons/arrow-down-circle.svg",
Self::Boom => "icons/boom.svg",
Self::Book => "icons/book.svg",
Self::Ban => "icons/ban.svg",
Self::ChevronDown => "icons/chevron-down.svg",
Self::CaretDown => "icons/caret-down.svg",
Self::CaretRight => "icons/caret-right.svg",
@@ -131,6 +134,7 @@ impl IconNamed for IconName {
Self::Link => "icons/link.svg",
Self::Loader => "icons/loader.svg",
Self::Lock => "icons/lock.svg",
Self::Login => "icons/login.svg",
Self::Logout => "icons/logout.svg",
Self::Message => "icons/message.svg",
Self::Moon => "icons/moon.svg",
+31 -6
View File
@@ -204,15 +204,27 @@ impl Workspace {
});
});
// Listen app-wide, so a group panel can toggle the sidebar without holding it.
// Listen app-wide, so a group panel can show or hide the sidebar without holding it.
let workspace = cx.entity().downgrade();
App::on_action(cx, move |action: &nip29_ui::ToggleGroupMeta, cx| {
App::on_action(cx, {
let workspace = workspace.clone();
move |action: &nip29_ui::ShowGroupMeta, cx| {
let Some(workspace) = workspace.upgrade() else {
return;
};
workspace.update(cx, |this, cx| this.show_group_meta(action, cx));
}
});
App::on_action(cx, move |action: &nip29_ui::HideGroupMeta, cx| {
let Some(workspace) = workspace.upgrade() else {
return;
};
workspace.update(cx, |this, cx| this.toggle_group_meta(action, cx));
workspace.update(cx, |this, cx| this.hide_group_meta(action, cx));
});
Self {
@@ -397,8 +409,8 @@ impl Workspace {
}
}
/// Show or hide the sidebar's information for the group a panel belongs to.
fn toggle_group_meta(&mut self, action: &nip29_ui::ToggleGroupMeta, cx: &mut Context<Self>) {
/// Show the sidebar's information for the group a panel belongs to.
fn show_group_meta(&mut self, action: &nip29_ui::ShowGroupMeta, cx: &mut Context<Self>) {
let Ok(id) = GroupId::new(action.group.clone()) else {
return;
};
@@ -406,7 +418,20 @@ impl Workspace {
let key = GroupKey::new(action.relay.clone(), id);
self.sidebar.update(cx, |sidebar, cx| {
sidebar.toggle_group(&key, cx);
sidebar.show_group_meta(&key, cx);
});
}
/// Hide the sidebar's information for the group a panel belongs to.
fn hide_group_meta(&mut self, action: &nip29_ui::HideGroupMeta, cx: &mut Context<Self>) {
let Ok(id) = GroupId::new(action.group.clone()) else {
return;
};
let key = GroupKey::new(action.relay.clone(), id);
self.sidebar.update(cx, |sidebar, cx| {
sidebar.hide_group_meta(&key, cx);
});
}
+12 -12
View File
@@ -190,28 +190,28 @@ impl Sidebar {
cx.notify();
}
/// Show or hide the group view depending on which group is being viewed.
pub(crate) fn toggle_group(&mut self, key: &GroupKey, cx: &mut Context<Self>) {
/// Show the group view without toggling it closed.
pub(crate) fn show_group_meta(&mut self, key: &GroupKey, cx: &mut Context<Self>) {
let groups = GroupsRegistry::global(cx);
let Some(entity) = groups.read(cx).group(key, cx) else {
let Some(group) = groups.read(cx).group(key, cx) else {
return;
};
let open = self
self.show_group(&group, cx);
}
/// Hide the group view if the sidebar is showing the given group.
pub(crate) fn hide_group_meta(&mut self, key: &GroupKey, cx: &mut Context<Self>) {
let showing = self
.group
.as_ref()
.and_then(|group| group.upgrade())
.is_some_and(|current| current.entity_id() == entity.entity_id());
.is_some_and(|group| group.read(cx).key() == key);
if open {
self.group_open = !self.group_open;
} else {
self.group = Some(entity.downgrade());
self.group_open = true;
if showing {
self.hide_group(cx);
}
cx.notify();
}
/// Leave the group view, returning the sidebar to its tab list.