render markdown message in community
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -19,6 +19,7 @@ use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock::{Panel, PanelEvent};
|
||||
use ui::input::{InputEvent, Textarea, TextareaState};
|
||||
use ui::markdown::RenderedText;
|
||||
use ui::message::WelcomeMessage;
|
||||
use ui::notification::Notification;
|
||||
use ui::scroll::Scrollbar;
|
||||
@@ -98,6 +99,8 @@ pub struct CommunityPanel {
|
||||
channel: Option<ChannelId>,
|
||||
/// The selected channel's timeline (oldest first)
|
||||
rows: Vec<ChatMessage>,
|
||||
/// Rendered markdown content, keyed by message id and dropped when a row changes
|
||||
rendered_texts_by_id: BTreeMap<EventId, RenderedText>,
|
||||
/// Whether the store holds rows older than `rows`
|
||||
has_more: bool,
|
||||
/// A round or a page read is in flight
|
||||
@@ -163,34 +166,32 @@ impl CommunityPanel {
|
||||
},
|
||||
));
|
||||
|
||||
let panel = Self {
|
||||
cx.defer_in(window, |this, window, cx| {
|
||||
this.list_state.set_follow_mode(FollowMode::Tail);
|
||||
this.list_state.set_scroll_handler(cx.listener(
|
||||
|this, event: &ListScrollEvent, window, cx| {
|
||||
if event.visible_range.start <= LOAD_OLDER_THRESHOLD {
|
||||
this.load_older(window, cx);
|
||||
}
|
||||
},
|
||||
));
|
||||
this.load(window, cx);
|
||||
});
|
||||
|
||||
Self {
|
||||
id,
|
||||
focus_handle: cx.focus_handle(),
|
||||
community: community.downgrade(),
|
||||
channel,
|
||||
rows: Vec::new(),
|
||||
rendered_texts_by_id: BTreeMap::new(),
|
||||
has_more: false,
|
||||
loading: false,
|
||||
list_state: ListState::new(0, ListAlignment::Bottom, px(1024.)),
|
||||
input,
|
||||
tasks: smallvec![],
|
||||
_subscriptions: subscriptions,
|
||||
};
|
||||
|
||||
panel.list_state.set_follow_mode(FollowMode::Tail);
|
||||
panel.list_state.set_scroll_handler(cx.listener(
|
||||
|this, event: &ListScrollEvent, window, cx| {
|
||||
if event.visible_range.start <= LOAD_OLDER_THRESHOLD {
|
||||
this.load_older(window, cx);
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
cx.defer_in(window, |this, window, cx| {
|
||||
this.load(window, cx);
|
||||
});
|
||||
|
||||
panel
|
||||
}
|
||||
}
|
||||
|
||||
/// The channel to show, following the community's selection.
|
||||
@@ -204,6 +205,7 @@ impl CommunityPanel {
|
||||
if channel != self.channel {
|
||||
self.channel = channel;
|
||||
self.rows.clear();
|
||||
self.rendered_texts_by_id.clear();
|
||||
self.has_more = false;
|
||||
self.loading = false;
|
||||
self.list_state.reset(2);
|
||||
@@ -493,6 +495,7 @@ impl CommunityPanel {
|
||||
|
||||
if !connected {
|
||||
self.rows = messages;
|
||||
self.rendered_texts_by_id.clear();
|
||||
self.has_more = has_more;
|
||||
self.list_state.reset(self.item_count());
|
||||
cx.notify();
|
||||
@@ -527,7 +530,13 @@ impl CommunityPanel {
|
||||
|
||||
for message in messages {
|
||||
match shown.get(&message.id).copied() {
|
||||
Some(ix) => self.rows[ix] = message,
|
||||
Some(ix) => {
|
||||
// Drop the cached render when the text changes, so edits re-parse.
|
||||
if self.rows[ix].content != message.content {
|
||||
self.rendered_texts_by_id.remove(&message.id);
|
||||
}
|
||||
self.rows[ix] = message;
|
||||
}
|
||||
None => fresh.push(message),
|
||||
}
|
||||
}
|
||||
@@ -690,7 +699,7 @@ impl CommunityPanel {
|
||||
fn render_message(
|
||||
&mut self,
|
||||
ix: usize,
|
||||
_window: &mut Window,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
if ix == 0 {
|
||||
@@ -708,7 +717,16 @@ impl CommunityPanel {
|
||||
|
||||
let show_author = self.opens_run(ix - 2);
|
||||
|
||||
message::render(ix, message, show_author, cx)
|
||||
let content = if message.deleted {
|
||||
message::deleted(cx)
|
||||
} else {
|
||||
self.rendered_texts_by_id
|
||||
.entry(message.id)
|
||||
.or_insert_with(|| RenderedText::new(&message.content, &[], true))
|
||||
.element(ix.into(), window, cx)
|
||||
};
|
||||
|
||||
message::render(ix, message, content, show_author, cx)
|
||||
}
|
||||
|
||||
fn render_composer(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
|
||||
@@ -12,7 +12,13 @@ use ui::avatar::Avatar;
|
||||
use ui::h_flex;
|
||||
use ui::message::MessageRow;
|
||||
|
||||
pub(crate) fn render(ix: usize, message: &ChatMessage, show_author: bool, cx: &App) -> AnyElement {
|
||||
pub(crate) fn render(
|
||||
ix: usize,
|
||||
message: &ChatMessage,
|
||||
content: AnyElement,
|
||||
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);
|
||||
@@ -30,23 +36,18 @@ pub(crate) fn render(ix: usize, message: &ChatMessage, show_author: bool, cx: &A
|
||||
.when(message.edited_at.is_some(), |this| {
|
||||
this.header_extra(div().child("(edited)"))
|
||||
})
|
||||
.child(content(message, cx))
|
||||
.child(content)
|
||||
.when(!message.reactions.is_empty(), |this| {
|
||||
this.child(reactions(message, cx))
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn content(message: &ChatMessage, cx: &App) -> AnyElement {
|
||||
if message.deleted {
|
||||
return div()
|
||||
.text_color(cx.theme().text_danger)
|
||||
.child("Message deleted")
|
||||
.into_any_element();
|
||||
}
|
||||
|
||||
/// The placeholder shown in place of the body of a deleted message.
|
||||
pub(crate) fn deleted(cx: &App) -> AnyElement {
|
||||
div()
|
||||
.child(SharedString::from(&message.content))
|
||||
.text_color(cx.theme().text_danger)
|
||||
.child("Message deleted")
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user