daydream

A small matrix web client written in rust
git clone git://archive.git.mtrnord.blog/daydream-mx/daydream.git
Log | Files | Refs | README | LICENSE

commit c9159791a7d70099c41455a00177d99115771165
parent 774306eb22d4e2e14e42735183651e5e3b249c47
Author: Marcel <mtrnord1@gmail.com>
Date:   Tue, 16 Jun 2020 20:32:15 +0200

Linkify links in non formatted body events

Took 22 minutes

Diffstat:
MCargo.toml | 3+++
Msrc/app/components/event_list.rs | 5+----
Msrc/app/components/events/notice.rs | 68++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/app/components/events/text.rs | 53+++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/app/matrix/sync.rs | 32++++++++++++++++++++------------
Msrc/app/matrix/types.rs | 1-
Msrc/utils/notifications.rs | 24++++++------------------
7 files changed, 125 insertions(+), 61 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -40,6 +40,9 @@ tr = { version = "0.1.3", default-features = false, features = ["gettext"]} i18n-embed = { version = "0.4", features = ["web-sys-requester"] } rust-embed = { version = "5", features = ["debug-embed", "compression"]} +# Make links links again! +linkify = "0.4.0" + # Used for lightboxes rand = "0.7" diff --git a/src/app/components/event_list.rs b/src/app/components/event_list.rs @@ -7,11 +7,8 @@ use matrix_sdk::{ use std::collections::HashMap; use yew::prelude::*; -use crate::app::components::events::{ - get_sender_avatar, image::Image, notice::Notice, text::Text, video::Video, -}; +use crate::app::components::events::{image::Image, notice::Notice, text::Text, video::Video}; use crate::app::matrix::{MatrixAgent, Request, Response}; -use crate::utils::notifications::Notifications; pub struct EventList { link: ComponentLink<Self>, diff --git a/src/app/components/events/notice.rs b/src/app/components/events/notice.rs @@ -1,8 +1,11 @@ +use linkify::LinkFinder; use matrix_sdk::{ events::room::message::{MessageEvent, NoticeMessageEventContent}, Room, }; +use web_sys::Node; use yew::prelude::*; +use yew::virtual_dom::VNode; use crate::app::components::events::{get_sender_displayname, is_new_user}; @@ -59,28 +62,53 @@ impl Component for Notice { "".to_string() }; - if new_user { - html! { - <p style="opacity: .6;"><displayname>{sender_displayname}{": "}</displayname> - { - self.props - .notice_event - .clone() - .unwrap().body.clone() - } - </p> + let mut pure_content = self.props.notice_event.clone().unwrap().body.clone(); + let finder = LinkFinder::new(); + let pure_content_clone = pure_content.clone(); + let links: Vec<_> = finder.links(&pure_content_clone).collect(); + + let content = if !links.is_empty() { + for link in links { + let html_link = format!("<a href={}>{}</a>", link.as_str(), link.as_str()); + pure_content.replace_range(link.start()..link.end(), &html_link); } + pure_content.clone() } else { - html! { - <p style="opacity: .6;"> - { - self.props - .notice_event - .clone() - .unwrap().body.clone() - } - </p> - } + pure_content + }; + + if new_user { + let full_html = format!( + "<p style=\"opacity: .6;\"><displayname>{}: </displayname>{}</p>", + sender_displayname, + content.clone() + ); + let js_text_event = { + let div = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("p") + .unwrap(); + div.set_inner_html(full_html.as_str()); + div + }; + let node = Node::from(js_text_event); + VNode::VRef(node) + } else { + let full_html = format!("<p style=\"opacity: .6;\">{}</p>", content.clone()); + let js_text_event = { + let div = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("p") + .unwrap(); + div.set_inner_html(full_html.as_str()); + div + }; + let node = Node::from(js_text_event); + VNode::VRef(node) } } } diff --git a/src/app/components/events/text.rs b/src/app/components/events/text.rs @@ -1,4 +1,5 @@ use crate::app::components::events::{get_sender_displayname, is_new_user}; +use linkify::LinkFinder; use matrix_sdk::{ events::room::message::{MessageEvent, TextMessageEventContent}, Room, @@ -59,6 +60,22 @@ impl Component for Text { } else { "".to_string() }; + + let mut pure_content = self.props.text_event.clone().unwrap().body.clone(); + let finder = LinkFinder::new(); + let pure_content_clone = pure_content.clone(); + let links: Vec<_> = finder.links(&pure_content_clone).collect(); + + let content = if !links.is_empty() { + for link in links { + let html_link = format!("<a href={}>{}</a>", link.as_str(), link.as_str()); + pure_content.replace_range(link.start()..link.end(), &html_link); + } + pure_content.clone() + } else { + pure_content + }; + if self.props.text_event.clone().unwrap().formatted.is_some() { let message = if new_user { format!( @@ -94,13 +111,37 @@ impl Component for Text { let node = Node::from(js_text_event); VNode::VRef(node) } else if new_user { - html! { - <p><displayname>{sender_displayname}{": "}</displayname>{self.props.text_event.clone().unwrap().body.clone()}</p> - } + let full_html = format!( + "<p><displayname>{}: </displayname>{}</p>", + sender_displayname, + content.clone() + ); + let js_text_event = { + let div = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("p") + .unwrap(); + div.set_inner_html(full_html.as_str()); + div + }; + let node = Node::from(js_text_event); + VNode::VRef(node) } else { - html! { - <p>{self.props.text_event.clone().unwrap().body.clone()}</p> - } + let full_html = format!("<p>{}</p>", content.clone()); + let js_text_event = { + let div = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("p") + .unwrap(); + div.set_inner_html(full_html.as_str()); + div + }; + let node = Node::from(js_text_event); + VNode::VRef(node) } } } diff --git a/src/app/matrix/sync.rs b/src/app/matrix/sync.rs @@ -60,18 +60,26 @@ impl Sync { let client = self.matrix_client.clone(); let local_room_id = room_id.clone(); spawn_local(async move { - let room: Arc<RwLock<Room>> = client.clone() - .get_joined_room(&local_room_id.clone()) - .await - .unwrap(); - let read_clone = room.read().await; - let clean_room = (*read_clone).clone(); - let avatar_url = - get_sender_avatar(homeserver_url, clean_room.clone(), cloned_event.clone()); - let displayname = get_sender_displayname(clean_room.clone(), cloned_event.clone()); - let notification = - Notifications::new(avatar_url, displayname, text_event.body.clone()); - notification.show(); + if Notifications::browser_support() { + let room: Arc<RwLock<Room>> = client + .clone() + .get_joined_room(&local_room_id.clone()) + .await + .unwrap(); + let read_clone = room.read().await; + let clean_room = (*read_clone).clone(); + let avatar_url = get_sender_avatar( + homeserver_url, + clean_room.clone(), + cloned_event.clone(), + ); + let displayname = + get_sender_displayname(clean_room.clone(), cloned_event.clone()); + + let notification = + Notifications::new(avatar_url, displayname, text_event.body.clone()); + notification.show(); + } }); } if let MessageEventContent::Image(mut image_event) = event.clone().content { diff --git a/src/app/matrix/types.rs b/src/app/matrix/types.rs @@ -1,4 +1,3 @@ -use matrix_sdk::Client; use url::Url; pub fn get_media_download_url(homeserver: &Url, mxc_url: String) -> String { diff --git a/src/utils/notifications.rs b/src/utils/notifications.rs @@ -1,10 +1,9 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use web_sys::{window, Notification, NotificationOptions, NotificationPermission, Window}; +use web_sys::{window, Notification, NotificationOptions, NotificationPermission}; #[derive(Clone)] pub(crate) struct Notifications { - window: Option<Window>, avatar: Option<String>, displayname: String, content: String, @@ -13,30 +12,19 @@ pub(crate) struct Notifications { impl Notifications { pub fn new(avatar: Option<String>, displayname: String, content: String) -> Self { Notifications { - window: None, avatar, displayname, content, } } - fn browser_support(&mut self) -> bool { - return match &self.window { - None => match window() { - Some(v) => { - self.window = window(); - match v.get("Notification") { - Some(_) => true, - _ => false, - } - } - - _ => false, - }, + pub fn browser_support() -> bool { + match window() { Some(v) => match v.get("Notification") { Some(_) => true, _ => false, }, - }; + _ => false, + } } fn notifications_allowed(&self) -> bool { @@ -64,7 +52,7 @@ impl Notifications { fn show_actual(&self) { let mut options = NotificationOptions::new() as NotificationOptions; - let mut options = options.body(&self.content).tag("daydream") as &mut NotificationOptions; + let options = options.body(&self.content).tag("daydream") as &mut NotificationOptions; let options = if self.avatar.is_some() { options.icon(self.avatar.as_ref().unwrap()) } else {