update member row

This commit is contained in:
2026-10-01 10:18:36 +07:00
parent 75b0f49d9e
commit a93be19e81
6 changed files with 103 additions and 263 deletions
+14 -13
View File
@@ -32,7 +32,6 @@ mod details;
mod invite;
mod message;
mod metadata;
mod users;
/// Ask the shell to show or hide the sidebar's information for a group.
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
@@ -172,7 +171,6 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
let info = group.clone();
let edit = group.clone();
let invite = group.clone();
let members = group.clone();
let leave = group;
h_flex()
@@ -199,7 +197,7 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
}),
)
.child(
Button::new("invite")
Button::new("group-invite")
.icon(IconName::Invite)
.tooltip("Invite people")
.ghost()
@@ -208,16 +206,6 @@ pub fn actions(group: WeakEntity<Group>, cx: &App) -> AnyElement {
invite::open(invite.clone(), window, cx);
}),
)
.child(
Button::new("members")
.icon(IconName::Group)
.tooltip("Members")
.ghost()
.small()
.on_click(move |_event, window, cx| {
users::open(members.clone(), window, cx);
}),
)
})
.child(
Button::new("leave")
@@ -268,6 +256,19 @@ pub fn confirm_leave(group: WeakEntity<Group>, window: &mut Window, cx: &mut App
});
}
/// Remove a member from the group and surface any relay error as a notification.
pub fn remove_member(
group: WeakEntity<Group>,
public_key: PublicKey,
window: &mut Window,
cx: &mut App,
) {
match group.update(cx, |group, cx| group.remove_user(public_key, cx)) {
Ok(task) => report(window, cx, task),
Err(error) => log::warn!("nip29: removing a group member failed: {error}"),
}
}
/// Await a group write and surface any relay error as a notification.
pub(crate) fn report(window: &mut Window, cx: &mut App, task: Task<Result<()>>) {
window
-207
View File
@@ -1,207 +0,0 @@
use gpui::prelude::FluentBuilder;
use gpui::{
AnyElement, App, AppContext, Entity, IntoElement, ParentElement, SharedString, Styled,
WeakEntity, Window, div, px,
};
use nip29::{Group, Role};
use nostr_sdk::prelude::*;
use person::PersonRegistry;
use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::button::{Button, ButtonVariants};
use ui::input::InputState;
use ui::notification::Notification;
use ui::switch::Switch;
use ui::{Sizable, StyledExt, WindowExtension, divider, h_flex, v_flex};
use crate::{field, report};
pub(crate) fn open(group: WeakEntity<Group>, window: &mut Window, cx: &mut App) {
let input = cx.new(|cx| InputState::new(window, cx).placeholder("npub1…"));
let selected = cx.new(|_cx| Vec::<String>::new());
window.open_dialog(cx, move |this, _window, cx| {
let group = group.clone();
let input = input.clone();
let selected = selected.clone();
let admins = group
.read_with(cx, |group, _cx| group.admins().entries().to_vec())
.unwrap_or_default();
let roles = group
.read_with(cx, |group, _cx| group.roles().roles().to_vec())
.unwrap_or_default();
let chosen = selected.read(cx).clone();
this.width(px(460.))
.max_h(px(620.))
.show_close(true)
.title("Members and roles")
.child(
v_flex()
.gap_4()
.child(
v_flex()
.gap_2()
.child(div().text_sm().font_medium().child("Admins"))
.when(admins.is_empty(), |this| {
this.child(
div()
.text_sm()
.text_color(cx.theme().text_placeholder)
.child("The relay lists no admins."),
)
})
.children(
admins
.iter()
.map(|(key, roles)| admin(key, roles, &group, cx)),
),
)
.child(divider(cx))
.child(
v_flex()
.gap_3()
.child(div().text_sm().font_medium().child("Add a member"))
.child(field("Public key", &input, cx))
.when(!roles.is_empty(), |this| {
this.child(
v_flex().gap_2().children(
roles
.iter()
.map(|role| role_switch(role, &selected, &chosen)),
),
)
})
.child(Button::new("add-user").label("Add").small().on_click({
let group = group.clone();
let input = input.clone();
let selected = selected.clone();
move |_event, window, cx| {
let value = input.read(cx).value().trim().to_owned();
let public_key = match PublicKey::parse(&value) {
Ok(public_key) => public_key,
Err(error) => {
window.push_notification(
Notification::error(error.to_string()),
cx,
);
return;
}
};
let roles = selected.read(cx).clone();
match group.update(cx, |group, cx| {
group.put_user(public_key, roles, cx)
}) {
Ok(task) => report(window, cx, task),
Err(error) => log::warn!(
"nip29: adding a group member failed: {error}"
),
}
input.update(cx, |input, cx| {
input.set_value("", window, cx);
});
selected.update(cx, |selected, cx| {
selected.clear();
cx.notify();
});
}
})),
),
)
});
}
fn admin(
public_key: &PublicKey,
roles: &[String],
group: &WeakEntity<Group>,
cx: &App,
) -> AnyElement {
let persons = PersonRegistry::global(cx);
let person = persons.read(cx).get(public_key, cx);
let public_key = *public_key;
h_flex()
.justify_between()
.gap_2()
.child(
h_flex()
.gap_2()
.min_w_0()
.child(
Avatar::new(person.avatar())
.seed(person.avatar_seed())
.xsmall(),
)
.child(
v_flex()
.min_w_0()
.child(div().truncate().child(person.name()))
.when(!roles.is_empty(), |this| {
this.child(
div()
.truncate()
.text_xs()
.text_color(cx.theme().text_placeholder)
.child(SharedString::from(roles.join(", "))),
)
}),
),
)
.child(
Button::new(SharedString::from(format!("del-{}", public_key.to_hex())))
.label("Remove")
.ghost()
.small()
.on_click({
let group = group.clone();
move |_event, window, cx| match group
.update(cx, |group, cx| group.remove_user(public_key, cx))
{
Ok(task) => report(window, cx, task),
Err(error) => {
log::warn!("nip29: removing a group member failed: {error}")
}
}
}),
)
.into_any_element()
}
fn role_switch(role: &Role, selected: &Entity<Vec<String>>, chosen: &[String]) -> AnyElement {
let name = role.name.clone();
let description = role.description.clone();
Switch::new(SharedString::from(format!("role-{}", role.name)))
.label(role.name.clone())
.when_some(description, |this, description| {
this.description(description)
})
.small()
.checked(chosen.contains(&role.name))
.on_click({
let selected = selected.clone();
move |next, _window, cx| {
selected.update(cx, |selected, cx| {
if *next {
if !selected.contains(&name) {
selected.push(name.clone());
}
} else {
selected.retain(|role| role != &name);
}
cx.notify();
});
}
})
.into_any_element()
}