feat: show facepill on tabpanel

This commit is contained in:
2025-01-16 08:49:24 +07:00
parent 161e6a5342
commit b119722392
9 changed files with 59 additions and 2051 deletions

1
Cargo.lock generated
View File

@@ -5629,7 +5629,6 @@ dependencies = [
"gpui", "gpui",
"image", "image",
"itertools 0.13.0", "itertools 0.13.0",
"nostr-sdk",
"once_cell", "once_cell",
"paste", "paste",
"regex", "regex",

View File

@@ -35,7 +35,7 @@ impl RenderOnce for Message {
.border_l_2() .border_l_2()
.border_color(cx.theme().background) .border_color(cx.theme().background)
.hover(|this| { .hover(|this| {
this.bg(cx.theme().accent.step(cx, ColorScaleStep::TWO)) this.bg(cx.theme().accent.step(cx, ColorScaleStep::ONE))
.border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE)) .border_color(cx.theme().accent.step(cx, ColorScaleStep::NINE))
}) })
.child( .child(

View File

@@ -386,8 +386,15 @@ impl Panel for ChatPanel {
self.id.clone() self.id.clone()
} }
fn panel_metadata(&self) -> Option<Metadata> { fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>> {
None Some(
self.room
.read(cx)
.members
.iter()
.map(|member| member.avatar())
.collect(),
)
} }
fn title(&self, _cx: &WindowContext) -> AnyElement { fn title(&self, _cx: &WindowContext) -> AnyElement {

View File

@@ -13,7 +13,6 @@ smallvec.workspace = true
anyhow.workspace = true anyhow.workspace = true
itertools.workspace = true itertools.workspace = true
chrono.workspace = true chrono.workspace = true
nostr-sdk.workspace = true
paste = "1" paste = "1"
regex = "1" regex = "1"

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,6 @@ use gpui::{
AnyElement, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Global, Hsla, AnyElement, AnyView, AppContext, EventEmitter, FocusHandle, FocusableView, Global, Hsla,
IntoElement, SharedString, View, WeakView, WindowContext, IntoElement, SharedString, View, WeakView, WindowContext,
}; };
use nostr_sdk::prelude::Metadata;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
pub enum PanelEvent { pub enum PanelEvent {
@@ -38,8 +37,8 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
/// Once you have defined a panel id, this must not be changed. /// Once you have defined a panel id, this must not be changed.
fn panel_id(&self) -> SharedString; fn panel_id(&self) -> SharedString;
/// The optional metadata of the panel /// The optional facepile of the panel
fn panel_metadata(&self) -> Option<Metadata> { fn panel_facepile(&self, _cx: &WindowContext) -> Option<Vec<String>> {
None None
} }
@@ -76,7 +75,7 @@ pub trait Panel: EventEmitter<PanelEvent> + FocusableView {
pub trait PanelView: 'static + Send + Sync { pub trait PanelView: 'static + Send + Sync {
fn panel_id(&self, cx: &WindowContext) -> SharedString; fn panel_id(&self, cx: &WindowContext) -> SharedString;
fn panel_metadata(&self, cx: &WindowContext) -> Option<Metadata>; fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>>;
fn title(&self, _cx: &WindowContext) -> AnyElement; fn title(&self, _cx: &WindowContext) -> AnyElement;
fn closeable(&self, cx: &WindowContext) -> bool; fn closeable(&self, cx: &WindowContext) -> bool;
fn zoomable(&self, cx: &WindowContext) -> bool; fn zoomable(&self, cx: &WindowContext) -> bool;
@@ -92,8 +91,8 @@ impl<T: Panel> PanelView for View<T> {
self.read(cx).panel_id() self.read(cx).panel_id()
} }
fn panel_metadata(&self, cx: &WindowContext) -> Option<Metadata> { fn panel_facepile(&self, cx: &WindowContext) -> Option<Vec<String>> {
self.read(cx).panel_metadata() self.read(cx).panel_facepile(cx)
} }
fn title(&self, cx: &WindowContext) -> AnyElement { fn title(&self, cx: &WindowContext) -> AnyElement {

View File

@@ -516,26 +516,25 @@ impl TabPanel {
.gap_1() .gap_1()
.text_ellipsis() .text_ellipsis()
.text_xs() .text_xs()
.child(div().when_some( .when_some(panel.panel_facepile(cx), |this, facepill| {
panel.panel_metadata(cx), this.child(
|this, metadata| { div()
if let Some(picture) = metadata.picture { .flex()
this.flex_shrink_0().child( .flex_row_reverse()
img(format!( .items_center()
"https://wsrv.nl/?url={}&w=100&h=100&n=-1", .justify_start()
picture .children(facepill.into_iter().enumerate().rev().map(
)) |(ix, face)| {
.size_4() div().when(ix > 0, |div| div.ml_neg_1()).child(
.rounded_full() img(face)
.object_fit(ObjectFit::Cover), .size_4()
) .rounded_full()
} else { .object_fit(ObjectFit::Cover),
this.flex_shrink_0().child( )
img("brand/avatar.png").size_4().rounded_full(), },
) )),
} )
}, })
))
.child(panel.title(cx)), .child(panel.title(cx)),
) )
.when(state.draggable, |this| { .when(state.draggable, |this| {
@@ -595,7 +594,7 @@ impl TabPanel {
active = false; active = false;
} }
Tab::new(("tab", ix), panel.title(cx), panel.panel_metadata(cx)) Tab::new(("tab", ix), panel.title(cx), panel.panel_facepile(cx))
.py_2() .py_2()
.selected(active) .selected(active)
.disabled(disabled) .disabled(disabled)

View File

@@ -1,6 +1,5 @@
use std::time::Duration;
use gpui::{ModelContext, Timer}; use gpui::{ModelContext, Timer};
use std::time::Duration;
static INTERVAL: Duration = Duration::from_millis(500); static INTERVAL: Duration = Duration::from_millis(500);
static PAUSE_DELAY: Duration = Duration::from_millis(300); static PAUSE_DELAY: Duration = Duration::from_millis(300);
@@ -72,6 +71,7 @@ impl BlinkCursor {
// delay 500ms to start the blinking // delay 500ms to start the blinking
let epoch = self.next_epoch(); let epoch = self.next_epoch();
cx.spawn(|this, mut cx| async move { cx.spawn(|this, mut cx| async move {
Timer::after(PAUSE_DELAY).await; Timer::after(PAUSE_DELAY).await;

View File

@@ -3,7 +3,6 @@ use crate::theme::ActiveTheme;
use crate::Selectable; use crate::Selectable;
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::*; use gpui::*;
use nostr_sdk::prelude::*;
pub mod tab_bar; pub mod tab_bar;
@@ -12,7 +11,7 @@ pub struct Tab {
id: ElementId, id: ElementId,
base: Stateful<Div>, base: Stateful<Div>,
label: AnyElement, label: AnyElement,
metadata: Option<Metadata>, facepill: Option<Vec<String>>,
prefix: Option<AnyElement>, prefix: Option<AnyElement>,
suffix: Option<AnyElement>, suffix: Option<AnyElement>,
disabled: bool, disabled: bool,
@@ -23,7 +22,7 @@ impl Tab {
pub fn new( pub fn new(
id: impl Into<ElementId>, id: impl Into<ElementId>,
label: impl IntoElement, label: impl IntoElement,
metadata: Option<Metadata>, facepill: Option<Vec<String>>,
) -> Self { ) -> Self {
let id: ElementId = id.into(); let id: ElementId = id.into();
@@ -31,11 +30,11 @@ impl Tab {
id: id.clone(), id: id.clone(),
base: div().id(id), base: div().id(id),
label: label.into_any_element(), label: label.into_any_element(),
metadata,
disabled: false, disabled: false,
selected: false, selected: false,
prefix: None, prefix: None,
suffix: None, suffix: None,
facepill,
} }
} }
@@ -143,19 +142,25 @@ impl RenderOnce for Tab {
.gap_1() .gap_1()
.text_ellipsis() .text_ellipsis()
.text_xs() .text_xs()
.child(div().when_some(self.metadata, |this, metadata| { .when_some(self.facepill, |this, facepill| {
if let Some(picture) = metadata.picture { this.child(
this.flex_shrink_0().child( div()
img(format!("https://wsrv.nl/?url={}&w=100&h=100&n=-1", picture)) .flex()
.size_4() .flex_row_reverse()
.rounded_full() .items_center()
.object_fit(ObjectFit::Cover), .justify_start()
) .children(facepill.into_iter().enumerate().rev().map(
} else { |(ix, face)| {
this.flex_shrink_0() div().when(ix > 0, |div| div.ml_neg_1()).child(
.child(img("brand/avatar.png").size_4().rounded_full()) img(face)
} .size_4()
})) .rounded_full()
.object_fit(ObjectFit::Cover),
)
},
)),
)
})
.child(self.label), .child(self.label),
) )
.when_some(self.suffix, |this, suffix| this.child(suffix)) .when_some(self.suffix, |this, suffix| this.child(suffix))