= OnceLock::new();
@@ -1077,7 +1117,7 @@ pub fn is_user_ignored(user_id: &UserId) -> bool {
/// Returns three channel endpoints related to the timeline for the given room.
-///
+///
/// 1. A timeline update sender.
/// 2. The timeline update receiver, which is a singleton, and can only be taken once.
/// 3. A `tokio::watch` sender that can be used to send requests to the timeline subscriber handler.
@@ -1861,191 +1901,188 @@ async fn timeline_subscriber_handler(
// Handle updates to the actual timeline content.
batch_opt = subscriber.next() => {
- if let Some(batch) = batch_opt {
- let mut num_updates = 0;
- // For now we always requery the latest event, but this can be better optimized.
- let mut reobtain_latest_event = true;
- let mut index_of_first_change = usize::MAX;
- let mut index_of_last_change = usize::MIN;
- // whether to clear the entire cache of drawn items
- let mut clear_cache = false;
- // whether the changes include items being appended to the end of the timeline
- let mut is_append = false;
- for diff in batch {
- num_updates += 1;
- match diff {
- VectorDiff::Append { values } => {
- let _values_len = values.len();
- index_of_first_change = min(index_of_first_change, timeline_items.len());
- timeline_items.extend(values);
- index_of_last_change = max(index_of_last_change, timeline_items.len());
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Append {_values_len}. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
- is_append = true;
+ let Some(batch) = batch_opt else { break };
+ let mut num_updates = 0;
+ // For now we always requery the latest event, but this can be better optimized.
+ let mut reobtain_latest_event = true;
+ let mut index_of_first_change = usize::MAX;
+ let mut index_of_last_change = usize::MIN;
+ // whether to clear the entire cache of drawn items
+ let mut clear_cache = false;
+ // whether the changes include items being appended to the end of the timeline
+ let mut is_append = false;
+ for diff in batch {
+ num_updates += 1;
+ match diff {
+ VectorDiff::Append { values } => {
+ let _values_len = values.len();
+ index_of_first_change = min(index_of_first_change, timeline_items.len());
+ timeline_items.extend(values);
+ index_of_last_change = max(index_of_last_change, timeline_items.len());
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Append {_values_len}. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ is_append = true;
+ }
+ VectorDiff::Clear => {
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Clear"); }
+ clear_cache = true;
+ timeline_items.clear();
+ reobtain_latest_event = true;
+ }
+ VectorDiff::PushFront { value } => {
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushFront"); }
+ if let Some((index, _ev)) = found_target_event_id.as_mut() {
+ *index += 1; // account for this new `value` being prepended.
+ } else {
+ found_target_event_id = find_target_event(&mut target_event_id, std::iter::once(&value));
}
- VectorDiff::Clear => {
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Clear"); }
- clear_cache = true;
- timeline_items.clear();
- reobtain_latest_event = true;
+
+ clear_cache = true;
+ timeline_items.push_front(value);
+ reobtain_latest_event |= latest_event.is_none();
+ }
+ VectorDiff::PushBack { value } => {
+ index_of_first_change = min(index_of_first_change, timeline_items.len());
+ timeline_items.push_back(value);
+ index_of_last_change = max(index_of_last_change, timeline_items.len());
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ is_append = true;
+ }
+ VectorDiff::PopFront => {
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopFront"); }
+ clear_cache = true;
+ timeline_items.pop_front();
+ if let Some((i, _ev)) = found_target_event_id.as_mut() {
+ *i = i.saturating_sub(1); // account for the first item being removed.
}
- VectorDiff::PushFront { value } => {
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushFront"); }
- if let Some((index, _ev)) = found_target_event_id.as_mut() {
- *index += 1; // account for this new `value` being prepended.
- } else {
- found_target_event_id = find_target_event(&mut target_event_id, std::iter::once(&value));
- }
-
+ // This doesn't affect whether we should reobtain the latest event.
+ }
+ VectorDiff::PopBack => {
+ timeline_items.pop_back();
+ index_of_first_change = min(index_of_first_change, timeline_items.len());
+ index_of_last_change = usize::MAX;
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ }
+ VectorDiff::Insert { index, value } => {
+ if index == 0 {
clear_cache = true;
- timeline_items.push_front(value);
- reobtain_latest_event |= latest_event.is_none();
+ } else {
+ index_of_first_change = min(index_of_first_change, index);
+ index_of_last_change = usize::MAX;
}
- VectorDiff::PushBack { value } => {
- index_of_first_change = min(index_of_first_change, timeline_items.len());
- timeline_items.push_back(value);
- index_of_last_change = max(index_of_last_change, timeline_items.len());
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PushBack. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
+ if index >= timeline_items.len() {
is_append = true;
}
- VectorDiff::PopFront => {
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopFront"); }
- clear_cache = true;
- timeline_items.pop_front();
- if let Some((i, _ev)) = found_target_event_id.as_mut() {
- *i = i.saturating_sub(1); // account for the first item being removed.
- }
- // This doesn't affect whether we should reobtain the latest event.
- }
- VectorDiff::PopBack => {
- timeline_items.pop_back();
- index_of_first_change = min(index_of_first_change, timeline_items.len());
- index_of_last_change = usize::MAX;
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff PopBack. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
- }
- VectorDiff::Insert { index, value } => {
- if index == 0 {
- clear_cache = true;
- } else {
- index_of_first_change = min(index_of_first_change, index);
- index_of_last_change = usize::MAX;
- }
- if index >= timeline_items.len() {
- is_append = true;
- }
- if let Some((i, _ev)) = found_target_event_id.as_mut() {
- // account for this new `value` being inserted before the previously-found target event's index.
- if index <= *i {
- *i += 1;
- }
- } else {
- found_target_event_id = find_target_event(&mut target_event_id, std::iter::once(&value))
- .map(|(i, ev)| (i + index, ev));
+ if let Some((i, _ev)) = found_target_event_id.as_mut() {
+ // account for this new `value` being inserted before the previously-found target event's index.
+ if index <= *i {
+ *i += 1;
}
-
- timeline_items.insert(index, value);
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
- }
- VectorDiff::Set { index, value } => {
- index_of_first_change = min(index_of_first_change, index);
- index_of_last_change = max(index_of_last_change, index.saturating_add(1));
- timeline_items.set(index, value);
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
+ } else {
+ found_target_event_id = find_target_event(&mut target_event_id, std::iter::once(&value))
+ .map(|(i, ev)| (i + index, ev));
}
- VectorDiff::Remove { index } => {
- if index == 0 {
- clear_cache = true;
- } else {
- index_of_first_change = min(index_of_first_change, index.saturating_sub(1));
- index_of_last_change = usize::MAX;
- }
- if let Some((i, _ev)) = found_target_event_id.as_mut() {
- // account for an item being removed before the previously-found target event's index.
- if index <= *i {
- *i = i.saturating_sub(1);
- }
- }
- timeline_items.remove(index);
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
+
+ timeline_items.insert(index, value);
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Insert at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ }
+ VectorDiff::Set { index, value } => {
+ index_of_first_change = min(index_of_first_change, index);
+ index_of_last_change = max(index_of_last_change, index.saturating_add(1));
+ timeline_items.set(index, value);
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Set at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ }
+ VectorDiff::Remove { index } => {
+ if index == 0 {
+ clear_cache = true;
+ } else {
+ index_of_first_change = min(index_of_first_change, index.saturating_sub(1));
+ index_of_last_change = usize::MAX;
}
- VectorDiff::Truncate { length } => {
- if length == 0 {
- clear_cache = true;
- } else {
- index_of_first_change = min(index_of_first_change, length.saturating_sub(1));
- index_of_last_change = usize::MAX;
+ if let Some((i, _ev)) = found_target_event_id.as_mut() {
+ // account for an item being removed before the previously-found target event's index.
+ if index <= *i {
+ *i = i.saturating_sub(1);
}
- timeline_items.truncate(length);
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); }
- reobtain_latest_event = true;
}
- VectorDiff::Reset { values } => {
- if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Reset, new length {}", values.len()); }
- clear_cache = true; // we must assume all items have changed.
- timeline_items = values;
- reobtain_latest_event = true;
+ timeline_items.remove(index);
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Remove at {index}. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
+ }
+ VectorDiff::Truncate { length } => {
+ if length == 0 {
+ clear_cache = true;
+ } else {
+ index_of_first_change = min(index_of_first_change, length.saturating_sub(1));
+ index_of_last_change = usize::MAX;
}
+ timeline_items.truncate(length);
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Truncate to length {length}. Changes: {index_of_first_change}..{index_of_last_change}"); }
+ reobtain_latest_event = true;
}
- }
-
- if num_updates > 0 {
- let new_latest_event = if reobtain_latest_event {
- timeline.latest_event().await
- } else {
- None
- };
-
- let changed_indices = index_of_first_change..index_of_last_change;
-
- if LOG_TIMELINE_DIFFS {
- log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. is_append? {is_append}, clear_cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len());
+ VectorDiff::Reset { values } => {
+ if LOG_TIMELINE_DIFFS { log!("timeline_subscriber: room {room_id} diff Reset, new length {}", values.len()); }
+ clear_cache = true; // we must assume all items have changed.
+ timeline_items = values;
+ reobtain_latest_event = true;
}
- timeline_update_sender.send(TimelineUpdate::NewItems {
- new_items: timeline_items.clone(),
- changed_indices,
- clear_cache,
- is_append,
+ }
+ }
+
+
+ if num_updates > 0 {
+ let new_latest_event = if reobtain_latest_event {
+ timeline.latest_event().await
+ } else {
+ None
+ };
+
+ let changed_indices = index_of_first_change..index_of_last_change;
+
+ if LOG_TIMELINE_DIFFS {
+ log!("timeline_subscriber: applied {num_updates} updates for room {room_id}, timeline now has {} items. is_append? {is_append}, clear_cache? {clear_cache}. Changes: {changed_indices:?}.", timeline_items.len());
+ }
+ timeline_update_sender.send(TimelineUpdate::NewItems {
+ new_items: timeline_items.clone(),
+ changed_indices,
+ clear_cache,
+ is_append,
unread_messages_count: None
- }).expect("Error: timeline update sender couldn't send update with new items!");
+ }).expect("Error: timeline update sender couldn't send update with new items!");
+
+ // We must send this update *after* the actual NewItems update,
+ // otherwise the UI thread (RoomScreen) won't be able to correctly locate the target event.
+ if let Some((index, found_event_id)) = found_target_event_id.take() {
+ target_event_id = None;
+ timeline_update_sender.send(
+ TimelineUpdate::TargetEventFound {
+ target_event_id: found_event_id.clone(),
+ index,
+ }
+ ).unwrap_or_else(
+ |_e| panic!("Error: timeline update sender couldn't send TargetEventFound({found_event_id}, {index}) to room {room_id}!")
+ );
+ }
- // We must send this update *after* the actual NewItems update,
- // otherwise the UI thread (RoomScreen) won't be able to correctly locate the target event.
- if let Some((index, found_event_id)) = found_target_event_id.take() {
- target_event_id = None;
- timeline_update_sender.send(
- TimelineUpdate::TargetEventFound {
- target_event_id: found_event_id.clone(),
- index,
- }
- ).unwrap_or_else(
- |_e| panic!("Error: timeline update sender couldn't send TargetEventFound({found_event_id}, {index}) to room {room_id}!")
- );
- }
+ // Send a Makepad-level signal to update this room's timeline UI view.
+ SignalToUI::set_ui_signal();
- // Send a Makepad-level signal to update this room's timeline UI view.
- SignalToUI::set_ui_signal();
-
- // Update the latest event for this room.
- if let Some(new_latest) = new_latest_event {
- if latest_event.as_ref().map_or(true, |ev| ev.timestamp() < new_latest.timestamp()) {
- let room_avatar_changed = update_latest_event(room_id.clone(), &new_latest);
- latest_event = Some(new_latest);
- if room_avatar_changed {
- spawn_fetch_room_avatar(room.clone());
- }
+ // Update the latest event for this room.
+ if let Some(new_latest) = new_latest_event {
+ if latest_event.as_ref().map_or(true, |ev| ev.timestamp() < new_latest.timestamp()) {
+ let room_avatar_changed = update_latest_event(room_id.clone(), &new_latest);
+ latest_event = Some(new_latest);
+ if room_avatar_changed {
+ spawn_fetch_room_avatar(room.clone());
}
}
}
- } else {
- break;
}
-
}
else => {
@@ -2084,6 +2121,9 @@ fn update_latest_event(
AnyOtherFullStateEventContent::RoomAvatar(_avatar_event) => {
room_avatar_changed = true;
}
+ AnyOtherFullStateEventContent::RoomPowerLevels(_power_level_event) => {
+ submit_async_request(MatrixRequest::CheckCanUserSendMessage { room_id: room_id.clone() })
+ }
_ => { }
}
}
@@ -2150,10 +2190,10 @@ async fn spawn_sso_server(
..Default::default()
};
Handle::current().spawn(async move {
- let Ok((client, client_session)) = build_client(&cli, app_data_dir()).await else {
- Cx::post_action(LoginAction::LoginFailure("Failed to establish client".to_string()));
- return;
- };
+ let Ok((client, client_session)) = build_client(&cli, app_data_dir()).await else {
+ Cx::post_action(LoginAction::LoginFailure("Failed to establish client".to_string()));
+ return;
+ };
let mut is_logged_in = false;
match client
.matrix_auth()
diff --git a/src/utils.rs b/src/utils.rs
index a893d57..c180190 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -189,9 +189,27 @@ pub const MEDIA_THUMBNAIL_FORMAT: MediaFormatConst = MediaFormatConst::Thumbnail
}
);
+/// Removes leading whitespace and HTML whitespace tags (`` and `
`) from the given `text`.
+pub fn trim_start_html_whitespace(mut text: &str) -> &str {
+ let mut prev_text_len = text.len();
+ loop {
+ text = text
+ .trim_start_matches("
")
+ .trim_start_matches("
")
+ .trim_start_matches("
")
+ .trim_start_matches("
")
+ .trim_start();
+
+ if text.len() == prev_text_len {
+ break;
+ }
+ prev_text_len = text.len();
+ }
+ text
+}
/// Looks for bare links in the given `text` and converts them into proper HTML links.
-pub fn linkify(text: &str) -> Cow<'_, str> {
+pub fn linkify(text: &str, is_html: bool) -> Cow<'_, str> {
use linkify::{LinkFinder, LinkKind};
let mut links = LinkFinder::new()
.links(text)
@@ -200,6 +218,15 @@ pub fn linkify(text: &str) -> Cow<'_, str> {
return Cow::Borrowed(text);
}
+ // A closure to escape text if it's not HTML.
+ let escaped = |text| {
+ if is_html {
+ Cow::from(text)
+ } else {
+ htmlize::escape_text(text)
+ }
+ };
+
let mut linkified_text = String::new();
let mut last_end_index = 0;
for link in links {
@@ -219,16 +246,18 @@ pub fn linkify(text: &str) -> Cow<'_, str> {
match link.kind() {
LinkKind::Url => {
linkified_text = format!(
- "{linkified_text}{}{}",
- text.get(last_end_index..link.start()).unwrap_or_default(),
+ "{linkified_text}{}{}",
+ escaped(text.get(last_end_index..link.start()).unwrap_or_default()),
htmlize::escape_attribute(link_txt),
+ htmlize::escape_text(link_txt),
);
}
LinkKind::Email => {
linkified_text = format!(
- "{linkified_text}{}{}",
- text.get(last_end_index..link.start()).unwrap_or_default(),
+ "{linkified_text}{}{}",
+ escaped(text.get(last_end_index..link.start()).unwrap_or_default()),
htmlize::escape_attribute(link_txt),
+ htmlize::escape_text(link_txt),
);
}
_ => return Cow::Borrowed(text), // unreachable
@@ -236,7 +265,10 @@ pub fn linkify(text: &str) -> Cow<'_, str> {
}
last_end_index = link.end();
}
- linkified_text.push_str(text.get(last_end_index..).unwrap_or_default());
+ linkified_text.push_str(
+ &escaped(text.get(last_end_index..).unwrap_or_default())
+ );
+ // makepad_widgets::log!("Original text:\n{:?}\nLinkified text:\n{:?}", text, linkified_text);
Cow::Owned(linkified_text)
}
@@ -287,14 +319,14 @@ mod tests_linkify {
#[test]
fn test_linkify0() {
let text = "Hello, world!";
- assert_eq!(linkify(text).as_ref(), text);
+ assert_eq!(linkify(text, false).as_ref(), text);
}
#[test]
fn test_linkify1() {
let text = "Check out this website: https://example.com";
let expected = "Check out this website: https://example.com";
- let actual = linkify(text);
+ let actual = linkify(text, false);
println!("{:?}", actual.as_ref());
assert_eq!(actual.as_ref(), expected);
}
@@ -303,7 +335,7 @@ mod tests_linkify {
fn test_linkify2() {
let text = "Send an email to john@example.com";
let expected = "Send an email to john@example.com";
- let actual = linkify(text);
+ let actual = linkify(text, false);
println!("{:?}", actual.as_ref());
assert_eq!(actual.as_ref(), expected);
}
@@ -311,14 +343,14 @@ mod tests_linkify {
#[test]
fn test_linkify3() {
let text = "Visit our website at www.example.com";
- assert_eq!(linkify(text).as_ref(), text);
+ assert_eq!(linkify(text, false).as_ref(), text);
}
#[test]
fn test_linkify4() {
let text = "Link 1 http://google.com Link 2 https://example.com";
let expected = "Link 1 http://google.com Link 2 https://example.com";
- let actual = linkify(text);
+ let actual = linkify(text, false);
println!("{:?}", actual.as_ref());
assert_eq!(actual.as_ref(), expected);
}
@@ -328,7 +360,7 @@ mod tests_linkify {
fn test_linkify5() {
let text = "html test Link title Link 2 https://example.com";
let expected = "html test Link title Link 2 https://example.com";
- let actual = linkify(text);
+ let actual = linkify(text, true);
println!("{:?}", actual.as_ref());
assert_eq!(actual.as_ref(), expected);
}
@@ -336,21 +368,21 @@ mod tests_linkify {
#[test]
fn test_linkify6() {
let text = "link title";
- assert_eq!(linkify(text).as_ref(), text);
+ assert_eq!(linkify(text, true).as_ref(), text);
}
#[test]
fn test_linkify7() {
let text = "https://example.com";
let expected = "https://example.com";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, false).as_ref(), expected);
}
#[test]
fn test_linkify8() {
let text = "test test https://crates.io/crates/cargo-packager test test";
let expected = "test test https://crates.io/crates/cargo-packager test test";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, false).as_ref(), expected);
}
#[test]
@@ -358,14 +390,14 @@ mod tests_linkify {
let text = "In reply to @spore:mozilla.org
So I asked if there's a crate for it (bc I don't have the time to test and debug it) or if there's simply a better way that involves less states and invariants
https://docs.rs/aho-corasick/latest/aho_corasick/struct.AhoCorasick.html#method.stream_find_iter";
let expected = "In reply to @spore:mozilla.org
So I asked if there's a crate for it (bc I don't have the time to test and debug it) or if there's simply a better way that involves less states and invariants
https://docs.rs/aho-corasick/latest/aho_corasick/struct.AhoCorasick.html#method.stream_find_iter";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, true).as_ref(), expected);
}
#[test]
fn test_linkify10() {
let text = "And then call read_until
or other BufRead
methods.";
let expected = "And then call read_until
or other BufRead
methods.";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, true).as_ref(), expected);
}
@@ -373,21 +405,21 @@ mod tests_linkify {
fn test_linkify11() {
let text = "And then https://google.com call read_until
or other BufRead
methods.";
let expected = "And then https://google.com call read_until
or other BufRead
methods.";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, true).as_ref(), expected);
}
#[test]
fn test_linkify12() {
let text = "And then https://google.com call read_until
or other BufRead http://another-link.http.com
methods.";
let expected = "And then https://google.com call read_until
or other BufRead http://another-link.http.com
methods.";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, true).as_ref(), expected);
}
#[test]
fn test_linkify13() {
let text = "Check out this website: https://example.com";
let expected = "Check out this website: https://example.com";
- assert_eq!(linkify(text).as_ref(), expected);
+ assert_eq!(linkify(text, true).as_ref(), expected);
}
}
diff --git a/src/verification.rs b/src/verification.rs
index 031a63f..84c0494 100644
--- a/src/verification.rs
+++ b/src/verification.rs
@@ -2,20 +2,23 @@ use std::sync::Arc;
use futures_util::StreamExt;
use makepad_widgets::{log, warning, ActionDefaultRef, Cx, DefaultNone};
use matrix_sdk::{
- crypto::{AcceptedProtocols, CancelInfo, EmojiShortAuthString}, encryption::verification::{
+ crypto::{AcceptedProtocols, CancelInfo, EmojiShortAuthString}, encryption::{verification::{
SasState, SasVerification, Verification, VerificationRequest,
VerificationRequestState,
- }, ruma::{
+ }, VerificationState}, ruma::{
events::{
- key::verification::{request::ToDeviceKeyVerificationRequestEvent, VerificationMethod},
- room::message::{MessageType, OriginalSyncRoomMessageEvent},
+ key::verification::{request::ToDeviceKeyVerificationRequestEvent, VerificationMethod}, room::message::{MessageType, OriginalSyncRoomMessageEvent}
},
UserId,
}, Client
};
use tokio::{runtime::Handle, sync::mpsc::{UnboundedReceiver, UnboundedSender}};
-
+#[derive(Clone, Debug, DefaultNone)]
+pub enum VerificationStateAction {
+ Update(VerificationState),
+ None,
+}
pub fn add_verification_event_handlers_and_sync_client(client: Client) {
let mut verification_state_subscriber = client.encryption().verification_state();
@@ -23,9 +26,10 @@ pub fn add_verification_event_handlers_and_sync_client(client: Client) {
Handle::current().spawn(async move {
while let Some(state) = verification_state_subscriber.next().await {
log!("Received a verification state update: {state:?}");
- // TODO: send an update to the main top-level app instance
- // such that we can display the verification state as an icon badge
- // atop the user's profile avatar.
+ Cx::post_action(VerificationStateAction::Update(state));
+ if let VerificationState::Verified = state {
+ break;
+ }
}
});
@@ -64,17 +68,6 @@ pub fn add_verification_event_handlers_and_sync_client(client: Client) {
}
}
);
-
- // This doesn't seem to be necessary, as we do receive verification requests
- // without this block.
- // The sliding sync service must be handling the synchronization already.
- //
- /*
- Handle::current().spawn(async move {
- client.sync(SyncSettings::new()).await
- .expect("Client sync loop failed");
- });
- */
}
@@ -154,7 +147,7 @@ async fn sas_verification_handler(
// confirmed their keys match the ones we have *before* we confirmed them.
log!("The other side confirmed that the displayed keys matched.");
};
-
+
}
SasState::Confirmed => Cx::post_action(VerificationAction::SasConfirmed),
@@ -173,7 +166,7 @@ async fn sas_verification_handler(
);
log!("[Post-verification] {}", dump_devices(sas.other_device().user_id(), &client).await);
// We go ahead and send the RequestCompleted action here,
- // because it is not guaranteed that the VerificationRequestState stream loop
+ // because it is not guaranteed that the VerificationRequestState stream loop
// will receive an update an enter the `Done` state.
Cx::post_action(VerificationAction::RequestCompleted);
break;
@@ -181,7 +174,7 @@ async fn sas_verification_handler(
SasState::Cancelled(cancel_info) => {
log!("SAS verification has been cancelled, reason: {}", cancel_info.reason());
// We go ahead and send the RequestCancelled action here,
- // because it is not guaranteed that the VerificationRequestState stream loop
+ // because it is not guaranteed that the VerificationRequestState stream loop
// will receive an update an enter the `Cancelled` state.
Cx::post_action(VerificationAction::RequestCancelled(cancel_info));
break;
@@ -299,7 +292,7 @@ pub enum VerificationAction {
}
/// The state included in a verification request action.
-///
+///
/// This is passed from the background async task to the main UI thread,
/// where it is extracted from the `VerificationAction` and then stored
/// in the `VerificationModal`` widget.
diff --git a/src/verification_modal.rs b/src/verification_modal.rs
index 78e0e29..fa7e86c 100644
--- a/src/verification_modal.rs
+++ b/src/verification_modal.rs
@@ -6,14 +6,13 @@ use matrix_sdk::encryption::verification::Verification;
use crate::verification::{VerificationAction, VerificationRequestActionState, VerificationUserResponse};
live_design! {
- import makepad_widgets::base::*;
- import makepad_widgets::theme_desktop_dark::*;
- import makepad_draw::shader::std::*;
+ use link::theme::*;
+ use link::widgets::*;
- import crate::shared::styles::*;
- import crate::shared::icon_button::RobrixIconButton;
+ use crate::shared::styles::*;
+ use crate::shared::icon_button::RobrixIconButton;
- VerificationModal = {{VerificationModal}} {
+ pub VerificationModal = {{VerificationModal}} {
width: Fit
height: Fit