add reveal text

This commit is contained in:
2026-10-30 14:03:14 +07:00
parent 905a339e3e
commit 41837e487b
5 changed files with 189 additions and 48 deletions
+43 -19
View File
@@ -28,7 +28,7 @@ use settings::AppSettings;
use smallvec::{SmallVec, smallvec};
use speech::speaker::Speaker;
use state::{NostrRegistry, pick_file, upload_file};
use ui::{Avatar, RenderedText, WelcomeMessage, rendered_text};
use ui::{Avatar, RenderedText, RevealText, WelcomeMessage, rendered_text};
use util::{attachment_name, display_name, opens_run, pin_label, report};
mod details;
@@ -323,6 +323,8 @@ pub struct GroupPanel {
speech_started: Option<instant::Instant>,
/// Reads messages aloud
speaker: Speaker,
/// The message being revealed in step with speech, with its estimated duration
speech_reveal: Option<(EventId, Duration)>,
/// Uploaded, non-encrypted attachments waiting to be sent.
attachments: Vec<Url>,
/// Whether an upload is in flight.
@@ -425,6 +427,20 @@ impl GroupPanel {
this.track_participants(cx);
});
// End the speech reveal when the utterance finishes ahead of its estimate
let speaker = Speaker::new();
let end_events = speaker.end_events();
let reveal_end_task = cx.spawn(async move |this, cx| {
while end_events.recv_async().await.is_ok() {
this.update(cx, |this, cx| {
this.speech_reveal = None;
cx.notify();
})?;
}
Ok(())
});
Self {
focus_handle: cx.focus_handle(),
group: group.downgrade(),
@@ -437,10 +453,11 @@ impl GroupPanel {
input,
speech,
speech_started: None,
speaker: Speaker::new(),
speaker,
speech_reveal: None,
attachments: Vec::new(),
uploading: false,
tasks: smallvec![],
tasks: smallvec![reveal_end_task],
_subscriptions: subscriptions,
}
}
@@ -1023,22 +1040,29 @@ impl GroupPanel {
let nostr = NostrRegistry::global(cx);
let directory = self.directory.clone();
let content = self
.rendered_texts_by_id
.entry(message.id)
.or_insert_with(|| {
rendered_text(
&message.content,
message
.mentions
.iter()
.map(|mention| (mention.range.clone(), mention.public_key)),
&message.emojis,
&directory,
cx,
)
})
.element(ix.into(), window, cx);
let content = match self
.speech_reveal
.as_ref()
.filter(|(reveal_id, _)| *reveal_id == message.id)
{
Some((_, duration)) => RevealText::new(message.preview(), *duration).into_any_element(),
None => self
.rendered_texts_by_id
.entry(message.id)
.or_insert_with(|| {
rendered_text(
&message.content,
message
.mentions
.iter()
.map(|mention| (mention.range.clone(), mention.public_key)),
&message.emojis,
&directory,
cx,
)
})
.element(ix.into(), window, cx),
};
let admin = nostr
.read(cx)
+7 -2
View File
@@ -91,11 +91,16 @@ pub(crate) fn render(
let speech_panel = actions.panel;
let panel = speech_panel.clone();
let speech_text = message.preview();
let speech_id = message.id;
let row = row.overlay(MessageActions::new().speech(move |_event, _window, cx| {
_ = speech_panel.update_in(cx, |panel, window, cx| {
if let Err(error) = panel.speaker.speak(&speech_text) {
window.push_notification(Notification::error(error.to_string()), cx);
match panel.speaker.speak(&speech_text) {
Ok(duration) => {
panel.speech_reveal = Some((speech_id, duration));
cx.notify();
}
Err(error) => window.push_notification(Notification::error(error.to_string()), cx),
}
});
}));