feat: add support for basic web of trust #65
@@ -10,6 +10,7 @@ use gpui::{
|
|||||||
StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, Window, div, px, svg,
|
StatefulInteractiveElement, Styled, Subscription, Task, WeakEntity, Window, div, px, svg,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
|
use person::PersonRegistry;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::{NostrRegistry, StateEvent};
|
use state::{NostrRegistry, StateEvent};
|
||||||
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius};
|
use theme::{ActiveTheme, TextStyle, Typeset as _, button_radius};
|
||||||
@@ -57,13 +58,14 @@ pub struct GreeterPanel {
|
|||||||
/// The coalesced reload scheduled after registry updates
|
/// The coalesced reload scheduled after registry updates
|
||||||
refresh_task: Option<Task<Result<(), Error>>>,
|
refresh_task: Option<Task<Result<(), Error>>>,
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
_subscriptions: SmallVec<[Subscription; 2]>,
|
_subscriptions: SmallVec<[Subscription; 3]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GreeterPanel {
|
impl GreeterPanel {
|
||||||
fn new(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(dock: WeakEntity<DockArea>, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||||
let chat = ChatRegistry::global(cx);
|
let chat = ChatRegistry::global(cx);
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let wot = PersonRegistry::global(cx).read(cx).wot();
|
||||||
|
|
||||||
let mut subscriptions = smallvec![];
|
let mut subscriptions = smallvec![];
|
||||||
|
|
||||||
@@ -76,6 +78,13 @@ impl GreeterPanel {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
subscriptions.push(
|
||||||
|
// Redraw when the web of trust loads or changes
|
||||||
|
cx.subscribe(&wot, |_, _, _, cx| {
|
||||||
|
cx.notify();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
// Reload soon after rooms load, messages arrive, or requests change
|
// Reload soon after rooms load, messages arrive, or requests change
|
||||||
cx.observe(&chat, |this, _, cx| {
|
cx.observe(&chat, |this, _, cx| {
|
||||||
@@ -176,120 +185,137 @@ impl GreeterPanel {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_requests(&mut self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
fn render_requests(&mut self, cx: &mut Context<Self>) -> AnyElement {
|
||||||
if self.requests.is_empty() {
|
// Spam requests stay out of the greeter entirely
|
||||||
return None;
|
let requests: Vec<_> = self
|
||||||
}
|
.requests
|
||||||
|
.iter()
|
||||||
|
.filter(|(room, _)| !room.read(cx).is_spam(cx))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
|
||||||
let dock = self.dock.clone();
|
let dock = self.dock.clone();
|
||||||
|
|
||||||
let rows: Vec<AnyElement> = self
|
let rows: Vec<AnyElement> = if requests.is_empty() {
|
||||||
.requests
|
vec![
|
||||||
.iter()
|
|
||||||
.take(SECTION_SIZE)
|
|
||||||
.enumerate()
|
|
||||||
.map(|(index, (room, preview))| {
|
|
||||||
let room = room.clone();
|
|
||||||
let dock = dock.clone();
|
|
||||||
let name = room.read(cx).display_name(cx);
|
|
||||||
let picture = room.read(cx).display_image(cx);
|
|
||||||
let seed = room.read(cx).display_image_seed(cx);
|
|
||||||
let created_at = room.read(cx).created_at.to_ago();
|
|
||||||
let preview = preview.clone();
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id(format!("request-{index}"))
|
.h_16()
|
||||||
.group("")
|
|
||||||
.w_full()
|
.w_full()
|
||||||
.h_9()
|
.justify_center()
|
||||||
.px_1p5()
|
.border_1()
|
||||||
.gap_2()
|
.border_dashed()
|
||||||
|
.border_color(cx.theme().border)
|
||||||
.rounded(px(button_radius()))
|
.rounded(px(button_radius()))
|
||||||
.cursor_pointer()
|
.text_style(TextStyle::Caption)
|
||||||
.hover(|this| this.bg(cx.theme().element_hover))
|
.text_color(cx.theme().text_faint)
|
||||||
.on_click({
|
.child("No chat requests")
|
||||||
let room = room.clone();
|
.into_any_element(),
|
||||||
let dock = dock.clone();
|
]
|
||||||
move |_, window, cx| {
|
} else {
|
||||||
open_room(&room, &dock, window, cx);
|
requests
|
||||||
}
|
.iter()
|
||||||
})
|
.enumerate()
|
||||||
.child(
|
.map(|(index, (room, preview))| {
|
||||||
Avatar::from_source(picture)
|
let room = room.clone();
|
||||||
.seed(seed)
|
let dock = dock.clone();
|
||||||
.small()
|
let name = room.read(cx).display_name(cx);
|
||||||
.flex_shrink_0(),
|
let picture = room.read(cx).display_image(cx);
|
||||||
)
|
let seed = room.read(cx).display_image_seed(cx);
|
||||||
.child(
|
let created_at = room.read(cx).created_at.to_ago();
|
||||||
div()
|
let preview = preview.clone();
|
||||||
.min_w_0()
|
|
||||||
.truncate()
|
|
||||||
.text_style(TextStyle::Callout)
|
|
||||||
.child(name),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.flex_1()
|
|
||||||
.min_w_0()
|
|
||||||
.truncate()
|
|
||||||
.text_style(TextStyle::Callout)
|
|
||||||
.text_color(cx.theme().text_muted)
|
|
||||||
.italic()
|
|
||||||
.child(preview),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.relative()
|
|
||||||
.flex_shrink_0()
|
|
||||||
.h_6()
|
|
||||||
.flex()
|
|
||||||
.items_center()
|
|
||||||
.justify_end()
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.absolute()
|
|
||||||
.left_0()
|
|
||||||
.right_0()
|
|
||||||
.top_0()
|
|
||||||
.bottom_0()
|
|
||||||
.group_hover("", |this| this.invisible())
|
|
||||||
.child(
|
|
||||||
div()
|
|
||||||
.text_align(gpui::TextAlign::Right)
|
|
||||||
.text_style(TextStyle::Caption)
|
|
||||||
.text_color(cx.theme().text_faint)
|
|
||||||
.child(created_at),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(
|
|
||||||
Button::new(format!("response-{index}"))
|
|
||||||
.label("Response")
|
|
||||||
.xsmall()
|
|
||||||
.ghost()
|
|
||||||
.invisible()
|
|
||||||
.group_hover("", |this| this.visible())
|
|
||||||
.on_click({
|
|
||||||
let room = room.clone();
|
|
||||||
let dock = dock.clone();
|
|
||||||
move |_, window, cx| {
|
|
||||||
cx.stop_propagation();
|
|
||||||
open_room(&room, &dock, window, cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_any_element()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Some(
|
h_flex()
|
||||||
GroupBox::new()
|
.id(format!("request-{index}"))
|
||||||
.id("requests")
|
.group("")
|
||||||
.title("Requests")
|
.w_full()
|
||||||
.fill()
|
.h_9()
|
||||||
.child(v_flex().gap_0().w_full().children(rows))
|
.px_1p5()
|
||||||
.into_any_element(),
|
.gap_2()
|
||||||
)
|
.rounded(px(button_radius()))
|
||||||
|
.cursor_pointer()
|
||||||
|
.hover(|this| this.bg(cx.theme().element_hover))
|
||||||
|
.on_click({
|
||||||
|
let room = room.clone();
|
||||||
|
let dock = dock.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
open_room(&room, &dock, window, cx);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.child(
|
||||||
|
Avatar::from_source(picture)
|
||||||
|
.seed(seed)
|
||||||
|
.small()
|
||||||
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.min_w_0()
|
||||||
|
.truncate()
|
||||||
|
.text_style(TextStyle::Callout)
|
||||||
|
.child(name),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.min_w_0()
|
||||||
|
.truncate()
|
||||||
|
.text_style(TextStyle::Callout)
|
||||||
|
.text_color(cx.theme().text_muted)
|
||||||
|
.italic()
|
||||||
|
.child(preview),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.relative()
|
||||||
|
.flex_shrink_0()
|
||||||
|
.h_6()
|
||||||
|
.flex()
|
||||||
|
.items_center()
|
||||||
|
.justify_end()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.absolute()
|
||||||
|
.left_0()
|
||||||
|
.right_0()
|
||||||
|
.top_0()
|
||||||
|
.bottom_0()
|
||||||
|
.group_hover("", |this| this.invisible())
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_align(gpui::TextAlign::Right)
|
||||||
|
.text_style(TextStyle::Caption)
|
||||||
|
.text_color(cx.theme().text_faint)
|
||||||
|
.child(created_at),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new(format!("response-{index}"))
|
||||||
|
.label("Response")
|
||||||
|
.xsmall()
|
||||||
|
.ghost()
|
||||||
|
.invisible()
|
||||||
|
.group_hover("", |this| this.visible())
|
||||||
|
.on_click({
|
||||||
|
let room = room.clone();
|
||||||
|
let dock = dock.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
cx.stop_propagation();
|
||||||
|
open_room(&room, &dock, window, cx);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_any_element()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
GroupBox::new()
|
||||||
|
.id("requests")
|
||||||
|
.title("Requests")
|
||||||
|
.fill()
|
||||||
|
.child(v_flex().gap_0().w_full().children(rows))
|
||||||
|
.into_any_element()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_follow_ups(&mut self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
fn render_follow_ups(&mut self, cx: &mut Context<Self>) -> Option<AnyElement> {
|
||||||
@@ -503,7 +529,7 @@ impl Render for GreeterPanel {
|
|||||||
.size_full()
|
.size_full()
|
||||||
.p_2()
|
.p_2()
|
||||||
.gap_6()
|
.gap_6()
|
||||||
.children(self.render_requests(cx))
|
.child(self.render_requests(cx))
|
||||||
.children(self.render_follow_ups(cx))
|
.children(self.render_follow_ups(cx))
|
||||||
.child(self.render_find_friends(cx)),
|
.child(self.render_find_friends(cx)),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user