This commit is contained in:
2026-09-23 07:40:37 +07:00
parent 366b289bf4
commit 7ca6634d69
3 changed files with 49 additions and 33 deletions
+22 -8
View File
@@ -27,10 +27,9 @@ mod message;
/// How near the top row a scroll has to come before the panel splices older history in.
const LOAD_OLDER_THRESHOLD: usize = 20;
/// A repeat message within this window keeps its run, so it carries no avatar or name.
const RUN_WINDOW_MS: u64 = 300_000;
/// Why a room is not showing the messages it knows about.
///
/// "No messages yet" is a claim, and a room we cannot read is not an empty one.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Notice {
Stranded,
@@ -59,23 +58,22 @@ impl fmt::Display for Notice {
),
Notice::MissingKey(epoch) => write!(
formatter,
"Messages here can't be read yet this channel's key for epoch {} is missing",
"Messages here can't be read yet, this channel's key for epoch {} is missing",
epoch.0
),
Notice::Unreachable => formatter.write_str("Couldn't reach the community's relays"),
Notice::Unreadable(1) => {
formatter.write_str("1 message here can't be read yet no key we hold opens it")
formatter.write_str("1 message here can't be read yet, no key we hold opens it")
}
Notice::Unreadable(count) => write!(
formatter,
"{count} messages here can't be read yet no key we hold opens them"
"{count} messages here can't be read yet, no key we hold opens them"
),
}
}
}
impl Notice {
/// Whether the room still accepts writes, rather than only being readable.
fn writable(self) -> bool {
matches!(self, Notice::Unreachable | Notice::Unreadable(_))
}
@@ -653,6 +651,20 @@ impl CommunityPanel {
.into_any_element()
}
/// Whether the row at `index` opens a run from one author.
fn opens_run(&self, index: usize) -> bool {
let Some(current) = self.rows.get(index) else {
return true;
};
let Some(previous) = index.checked_sub(1).and_then(|index| self.rows.get(index)) else {
return true;
};
current.author != previous.author
|| current.at_ms.saturating_sub(previous.at_ms) > RUN_WINDOW_MS
}
fn render_message(
&mut self,
ix: usize,
@@ -667,7 +679,9 @@ impl CommunityPanel {
return div().into_any_element();
};
message::render(ix, message, cx)
let show_author = self.opens_run(ix - 1);
message::render(ix, message, show_author, cx)
}
fn render_composer(&self, cx: &mut Context<Self>) -> impl IntoElement {
+25 -24
View File
@@ -4,7 +4,7 @@ use common::TimestampExt;
use community::ChatMessage;
use gpui::prelude::FluentBuilder;
use gpui::{
AnyElement, App, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div,
AnyElement, App, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div, px,
};
use nostr_sdk::prelude::*;
use person::PersonRegistry;
@@ -13,7 +13,7 @@ use theme::ActiveTheme;
use ui::avatar::Avatar;
use ui::{StyledExt, h_flex, v_flex};
pub(crate) fn render(ix: usize, message: &ChatMessage, cx: &App) -> AnyElement {
pub(crate) fn render(ix: usize, message: &ChatMessage, show_author: bool, cx: &App) -> AnyElement {
let persons = PersonRegistry::global(cx);
let author = persons.read(cx).get(&message.author, cx);
let hide_avatar = AppSettings::get_hide_avatar(cx);
@@ -29,35 +29,36 @@ pub(crate) fn render(ix: usize, message: &ChatMessage, cx: &App) -> AnyElement {
.items_start()
.gap_3()
.when(!hide_avatar, |this| {
this.child(
Avatar::new(author.avatar())
.seed(author.avatar_seed())
.flex_shrink_0(),
)
if show_author {
this.child(
Avatar::new(author.avatar())
.seed(author.avatar_seed())
.flex_shrink_0(),
)
} else {
this.child(div().flex_shrink_0().w(px(32.)))
}
})
.child(
v_flex()
.flex_1()
.min_w_0()
.gap_0p5()
.child(
h_flex()
.gap_2()
.text_sm()
.child(div().font_semibold().child(author.name()))
.child(
div()
.text_color(cx.theme().text_placeholder)
.child(Timestamp::from_secs(message.at_ms / 1000).to_ago()),
)
.when(message.edited_at.is_some(), |this| {
this.child(
div()
.text_color(cx.theme().text_placeholder)
.child("(edited)"),
.when(show_author, |this| {
this.child(
h_flex()
.gap_2()
.text_sm()
.text_color(cx.theme().text_placeholder)
.child(div().font_semibold().child(author.name()))
.child(
Timestamp::from_secs(message.at_ms / 1000).to_human_time(),
)
}),
)
.when(message.edited_at.is_some(), |this| {
this.child(div().child("(edited)"))
}),
)
})
.child(content(message, cx))
.when(!message.reactions.is_empty(), |this| {
this.child(reactions(message, cx))
+2 -1
View File
@@ -129,7 +129,6 @@ pub fn build_message(
build_rumor_ms(KIND_MESSAGE, author, content, tags, at_ms)
}
/// `parent` is the immediate parent; a `None` root means the parent is the thread's root.
#[allow(clippy::too_many_arguments)]
pub fn build_comment(
author: PublicKey,
@@ -146,12 +145,14 @@ pub fn build_comment(
tags.push(Tag::custom(TAG_ROOT_KIND, [root.kind.to_string()]));
tags.push(reply_tag(TAG_ROOT, &root.reply));
if let Some(root_author) = root.reply.author {
tags.push(Tag::custom(TAG_ROOT_AUTHOR, [root_author.to_hex()]));
}
tags.push(Tag::custom(TAG_TARGET_KIND, [parent.kind.to_string()]));
tags.push(reply_tag(TAG_TARGET, &parent.reply));
if let Some(parent_author) = parent.reply.author {
tags.push(Tag::custom(TAG_TARGET_AUTHOR, [parent_author.to_hex()]));
}