update
This commit is contained in:
@@ -27,10 +27,9 @@ mod message;
|
|||||||
|
|
||||||
/// How near the top row a scroll has to come before the panel splices older history in.
|
/// How near the top row a scroll has to come before the panel splices older history in.
|
||||||
const LOAD_OLDER_THRESHOLD: usize = 20;
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum Notice {
|
enum Notice {
|
||||||
Stranded,
|
Stranded,
|
||||||
@@ -59,23 +58,22 @@ impl fmt::Display for Notice {
|
|||||||
),
|
),
|
||||||
Notice::MissingKey(epoch) => write!(
|
Notice::MissingKey(epoch) => write!(
|
||||||
formatter,
|
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
|
epoch.0
|
||||||
),
|
),
|
||||||
Notice::Unreachable => formatter.write_str("Couldn't reach the community's relays"),
|
Notice::Unreachable => formatter.write_str("Couldn't reach the community's relays"),
|
||||||
Notice::Unreadable(1) => {
|
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!(
|
Notice::Unreadable(count) => write!(
|
||||||
formatter,
|
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 {
|
impl Notice {
|
||||||
/// Whether the room still accepts writes, rather than only being readable.
|
|
||||||
fn writable(self) -> bool {
|
fn writable(self) -> bool {
|
||||||
matches!(self, Notice::Unreachable | Notice::Unreadable(_))
|
matches!(self, Notice::Unreachable | Notice::Unreadable(_))
|
||||||
}
|
}
|
||||||
@@ -653,6 +651,20 @@ impl CommunityPanel {
|
|||||||
.into_any_element()
|
.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(
|
fn render_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
ix: usize,
|
ix: usize,
|
||||||
@@ -667,7 +679,9 @@ impl CommunityPanel {
|
|||||||
return div().into_any_element();
|
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 {
|
fn render_composer(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use common::TimestampExt;
|
|||||||
use community::ChatMessage;
|
use community::ChatMessage;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div,
|
AnyElement, App, InteractiveElement, IntoElement, ParentElement, SharedString, Styled, div, px,
|
||||||
};
|
};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
use person::PersonRegistry;
|
use person::PersonRegistry;
|
||||||
@@ -13,7 +13,7 @@ use theme::ActiveTheme;
|
|||||||
use ui::avatar::Avatar;
|
use ui::avatar::Avatar;
|
||||||
use ui::{StyledExt, h_flex, v_flex};
|
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 persons = PersonRegistry::global(cx);
|
||||||
let author = persons.read(cx).get(&message.author, cx);
|
let author = persons.read(cx).get(&message.author, cx);
|
||||||
let hide_avatar = AppSettings::get_hide_avatar(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()
|
.items_start()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.when(!hide_avatar, |this| {
|
.when(!hide_avatar, |this| {
|
||||||
this.child(
|
if show_author {
|
||||||
Avatar::new(author.avatar())
|
this.child(
|
||||||
.seed(author.avatar_seed())
|
Avatar::new(author.avatar())
|
||||||
.flex_shrink_0(),
|
.seed(author.avatar_seed())
|
||||||
)
|
.flex_shrink_0(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.child(div().flex_shrink_0().w(px(32.)))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
.min_w_0()
|
.min_w_0()
|
||||||
.gap_0p5()
|
.gap_0p5()
|
||||||
.child(
|
.when(show_author, |this| {
|
||||||
h_flex()
|
this.child(
|
||||||
.gap_2()
|
h_flex()
|
||||||
.text_sm()
|
.gap_2()
|
||||||
.child(div().font_semibold().child(author.name()))
|
.text_sm()
|
||||||
.child(
|
.text_color(cx.theme().text_placeholder)
|
||||||
div()
|
.child(div().font_semibold().child(author.name()))
|
||||||
.text_color(cx.theme().text_placeholder)
|
.child(
|
||||||
.child(Timestamp::from_secs(message.at_ms / 1000).to_ago()),
|
Timestamp::from_secs(message.at_ms / 1000).to_human_time(),
|
||||||
)
|
|
||||||
.when(message.edited_at.is_some(), |this| {
|
|
||||||
this.child(
|
|
||||||
div()
|
|
||||||
.text_color(cx.theme().text_placeholder)
|
|
||||||
.child("(edited)"),
|
|
||||||
)
|
)
|
||||||
}),
|
.when(message.edited_at.is_some(), |this| {
|
||||||
)
|
this.child(div().child("(edited)"))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
.child(content(message, cx))
|
.child(content(message, cx))
|
||||||
.when(!message.reactions.is_empty(), |this| {
|
.when(!message.reactions.is_empty(), |this| {
|
||||||
this.child(reactions(message, cx))
|
this.child(reactions(message, cx))
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ pub fn build_message(
|
|||||||
build_rumor_ms(KIND_MESSAGE, author, content, tags, at_ms)
|
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)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn build_comment(
|
pub fn build_comment(
|
||||||
author: PublicKey,
|
author: PublicKey,
|
||||||
@@ -146,12 +145,14 @@ pub fn build_comment(
|
|||||||
|
|
||||||
tags.push(Tag::custom(TAG_ROOT_KIND, [root.kind.to_string()]));
|
tags.push(Tag::custom(TAG_ROOT_KIND, [root.kind.to_string()]));
|
||||||
tags.push(reply_tag(TAG_ROOT, &root.reply));
|
tags.push(reply_tag(TAG_ROOT, &root.reply));
|
||||||
|
|
||||||
if let Some(root_author) = root.reply.author {
|
if let Some(root_author) = root.reply.author {
|
||||||
tags.push(Tag::custom(TAG_ROOT_AUTHOR, [root_author.to_hex()]));
|
tags.push(Tag::custom(TAG_ROOT_AUTHOR, [root_author.to_hex()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.push(Tag::custom(TAG_TARGET_KIND, [parent.kind.to_string()]));
|
tags.push(Tag::custom(TAG_TARGET_KIND, [parent.kind.to_string()]));
|
||||||
tags.push(reply_tag(TAG_TARGET, &parent.reply));
|
tags.push(reply_tag(TAG_TARGET, &parent.reply));
|
||||||
|
|
||||||
if let Some(parent_author) = parent.reply.author {
|
if let Some(parent_author) = parent.reply.author {
|
||||||
tags.push(Tag::custom(TAG_TARGET_AUTHOR, [parent_author.to_hex()]));
|
tags.push(Tag::custom(TAG_TARGET_AUTHOR, [parent_author.to_hex()]));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user