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 907abc2441805b391fe1badb6876a276109a9770
parent 750294ffc40c00856bf3c1ca563796425dfd3d49
Author: Marcel <mtrnord1@gmail.com>
Date:   Thu,  4 Jun 2020 13:44:02 +0200

Fix multiple clippy issues and prepare splitting events to extra components

Took 1 hour 36 minutes

Diffstat:
Msrc/app.rs | 1-
Msrc/app/components/event_list.rs | 95++++++++++++++++++++-----------------------------------------------------------
Asrc/app/components/events/image.rs | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/app/components/events/mod.rs | 26++++++++++++++++++++++++++
Asrc/app/components/events/notice.rs | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/app/components/events/text.rs | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/app/components/events/video.rs | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/app/components/mod.rs | 1+
Msrc/app/components/room_list.rs | 5++---
Msrc/app/matrix.rs | 42++++++++++++++++++++----------------------
Msrc/app/matrix/sync.rs | 9+++++----
Msrc/app/matrix/types.rs | 6+++---
Msrc/app/views/login.rs | 1-
Msrc/app/views/main_view.rs | 5++---
14 files changed, 318 insertions(+), 108 deletions(-)

diff --git a/src/app.rs b/src/app.rs @@ -1,4 +1,3 @@ -use log::*; use yew::{prelude::*, virtual_dom::VNode}; use yew_router::agent::RouteRequest::ChangeRoute; use yew_router::{prelude::*, Switch}; diff --git a/src/app/components/event_list.rs b/src/app/components/event_list.rs @@ -1,16 +1,14 @@ use std::collections::HashMap; -use std::convert::TryFrom; use log::*; use matrix_sdk::{ events::room::message::{MessageEvent, MessageEventContent}, - identifiers::{EventId, RoomId}, + identifiers::RoomId, Room, }; -use web_sys::Node; use yew::prelude::*; -use yew::virtual_dom::VNode; +use crate::app::components::events::text::Text; use crate::app::matrix::{MatrixAgent, Request, Response}; pub struct EventList { @@ -28,7 +26,7 @@ pub struct State { } pub enum Msg { - NewMessage(Response), + NewMessage(Response), SetMessage(String), SendMessage, Nope, @@ -56,7 +54,7 @@ impl Component for EventList { if props.current_room.is_some() { let room_id = props.current_room.clone().unwrap().room_id; if !state.events.contains_key(&room_id) { - matrix_agent.send(Request::GetOldMessages((room_id.clone(), None))); + matrix_agent.send(Request::GetOldMessages((room_id, None))); } } @@ -74,21 +72,14 @@ impl Component for EventList { match response { Response::Sync((room_id, msg)) => { // TODO handle all events - return if self.state.events.contains_key(&room_id) { + if self.state.events.contains_key(&room_id) { if !(self.state.events[&room_id] .iter() .map(|x| x.event_id.clone()) - .collect::<Vec<EventId>>() - .contains(&msg.event_id)) + .any(|x| x == msg.event_id)) { self.state.events.get_mut(&room_id).unwrap().push(msg); - return if room_id - == self.props.current_room.clone().unwrap().room_id - { - true - } else { - false - }; + room_id == self.props.current_room.clone().unwrap().room_id } else { false } @@ -96,14 +87,12 @@ impl Component for EventList { let mut msgs = Vec::new(); msgs.push(msg); self.state.events.insert(room_id.clone(), msgs); - return if room_id == self.props.current_room.clone().unwrap().room_id { - true - } else { - false - }; - }; + room_id == self.props.current_room.clone().unwrap().room_id + } } Response::OldMessages((room_id, mut messages)) => { + // This is a clippy false positive + #[allow(clippy::map_entry)] if self.state.events.contains_key(&room_id) { self.state .events @@ -146,7 +135,7 @@ impl Component for EventList { let room_id = props.clone().current_room.unwrap().room_id; if !self.state.events.contains_key(&room_id) { self.matrix_agent - .send(Request::GetOldMessages((room_id.clone(), None))); + .send(Request::GetOldMessages((room_id, None))); } } self.props = props; @@ -169,8 +158,8 @@ impl Component for EventList { if pos == 0 { elements.push(self.get_event(None, event)); } else { - elements.push(self.get_event(Some(&events[pos - 1]), event)); - }; + elements.push(self.get_event(Some(events[pos - 1].clone()), event)); + } } elements.into_iter().collect::<Html>() } else { @@ -202,15 +191,11 @@ impl Component for EventList { impl EventList { // Typeinspection of IDEA breaks with this :D //noinspection RsTypeCheck - fn get_event(&self, prev_event: Option<&MessageEvent>, event: &MessageEvent) -> Html { + fn get_event(&self, prev_event: Option<MessageEvent>, event: &MessageEvent) -> Html { // TODO make encryption supported let new_user = if prev_event.is_some() { - if prev_event.unwrap().sender.to_string() == event.sender.to_string() { - false - } else { - true - } + prev_event.clone().unwrap().sender != event.sender } else { true }; @@ -223,50 +208,18 @@ impl EventList { .display_name .as_ref() .map(ToString::to_string) - .unwrap_or(event.sender.to_string()), + .unwrap_or_else(||event.sender.to_string()), } }; match &event.content { MessageEventContent::Text(text_event) => { - if text_event.formatted_body.is_some() { - let message = if new_user { - format!( - "<displayname>{}:</displayname> {}", - sender_displayname, - text_event.formatted_body.as_ref().unwrap() - ) - } else { - format!( - "{}", - text_event.formatted_body.as_ref().unwrap() - ) - }; - let js_text_event = { - let div = web_sys::window() - .unwrap() - .document() - .unwrap() - .create_element("p") - .unwrap(); - div.set_inner_html( - message - .as_str(), - ); - div - }; - let node = Node::from(js_text_event); - let vnode = VNode::VRef(node); - vnode - } else { - if new_user { - html! { - <p><displayname>{sender_displayname}{": "}</displayname>{text_event.body.clone()}</p> - } - } else { - html! { - <p>{text_event.body.clone()}</p> - } - } + html! { + <Text + prev_event=prev_event.clone() + event=Some(event.clone()) + room=Some(self.props.current_room.clone().unwrap()) + text_event=Some(text_event.clone()) + /> } } MessageEventContent::Notice(notice_event) => { diff --git a/src/app/components/events/image.rs b/src/app/components/events/image.rs @@ -0,0 +1,41 @@ +use matrix_sdk::events::room::message::MessageEvent; +use yew::prelude::*; + +struct Image { + props: Props, +} + +#[derive(Clone, Properties, Debug)] +pub struct Props { + #[prop_or_default] + pub prev_event: Option<MessageEvent>, + #[prop_or_default] + pub event: Option<MessageEvent>, +} + +impl Component for Image { + type Message = (); + type Properties = Props; + + fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self { + Image { props } + } + + fn update(&mut self, _msg: Self::Message) -> bool { + false + } + + fn change(&mut self, props: Self::Properties) -> bool { + // TODO fix the PartialEq hack + if format!("{:#?}", self.props) != format!("{:#?}", props) { + self.props = props; + true + } else { + false + } + } + + fn view(&self) -> Html { + html! {} + } +} diff --git a/src/app/components/events/mod.rs b/src/app/components/events/mod.rs @@ -0,0 +1,26 @@ +use matrix_sdk::events::room::message::MessageEvent; +use matrix_sdk::Room; + +pub mod image; +pub mod notice; +pub mod text; +pub mod video; + +pub fn is_new_user(prev_event: Option<MessageEvent>, event: MessageEvent) -> bool { + if let Some(prev_event) = prev_event { + prev_event.sender != event.sender + } else { + true + } +} + +pub fn get_sender_displayname(room: Room, event: MessageEvent) -> String { + match room.members.get(&event.sender) { + None => event.sender.to_string(), + Some(member) => member + .display_name + .as_ref() + .map(ToString::to_string) + .unwrap_or_else(|| event.sender.to_string()), + } +} diff --git a/src/app/components/events/notice.rs b/src/app/components/events/notice.rs @@ -0,0 +1,41 @@ +use matrix_sdk::events::room::message::MessageEvent; +use yew::prelude::*; + +struct Notice { + props: Props, +} + +#[derive(Clone, Properties, Debug)] +pub struct Props { + #[prop_or_default] + pub prev_event: Option<MessageEvent>, + #[prop_or_default] + pub event: Option<MessageEvent>, +} + +impl Component for Notice { + type Message = (); + type Properties = Props; + + fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self { + Notice { props } + } + + fn update(&mut self, _msg: Self::Message) -> bool { + false + } + + fn change(&mut self, props: Self::Properties) -> bool { + // TODO fix the PartialEq hack + if format!("{:#?}", self.props) != format!("{:#?}", props) { + self.props = props; + true + } else { + false + } + } + + fn view(&self) -> Html { + html! {} + } +} diff --git a/src/app/components/events/text.rs b/src/app/components/events/text.rs @@ -0,0 +1,112 @@ +use crate::app::components::events::{get_sender_displayname, is_new_user}; +use matrix_sdk::{ + events::room::message::{MessageEvent, TextMessageEventContent}, + Room, +}; +use web_sys::Node; +use yew::prelude::*; +use yew::virtual_dom::VNode; + +pub struct Text { + props: Props, +} + +#[derive(Clone, Properties, Debug)] +pub struct Props { + #[prop_or_default] + pub prev_event: Option<MessageEvent>, + #[prop_or_default] + pub event: Option<MessageEvent>, + #[prop_or_default] + pub text_event: Option<TextMessageEventContent>, + #[prop_or_default] + pub room: Option<Room>, +} + +impl Component for Text { + type Message = (); + type Properties = Props; + + fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self { + Text { props } + } + + fn update(&mut self, _msg: Self::Message) -> bool { + false + } + + fn change(&mut self, props: Self::Properties) -> bool { + // TODO fix the PartialEq hack + if format!("{:#?}", self.props) != format!("{:#?}", props) { + self.props = props; + true + } else { + false + } + } + + //noinspection RsTypeCheck + fn view(&self) -> Html { + let new_user = is_new_user( + self.props.prev_event.clone(), + self.props.event.clone().unwrap(), + ); + let sender_displayname = if new_user { + get_sender_displayname( + self.props.room.clone().unwrap(), + self.props.event.clone().unwrap(), + ) + } else { + "".to_string() + }; + if self + .props + .text_event + .clone() + .unwrap() + .formatted_body + .is_some() + { + let message = if new_user { + format!( + "<displayname>{}:</displayname> {}", + sender_displayname, + self.props + .text_event + .clone() + .unwrap() + .formatted_body + .as_ref() + .unwrap() + ) + } else { + self.props + .text_event + .clone() + .unwrap() + .formatted_body + .unwrap() + }; + let js_text_event = { + let div = web_sys::window() + .unwrap() + .document() + .unwrap() + .create_element("p") + .unwrap(); + div.set_inner_html(message.as_str()); + div + }; + 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> + } + } else { + html! { + <p>{self.props.text_event.clone().unwrap().body.clone()}</p> + } + } + } +} diff --git a/src/app/components/events/video.rs b/src/app/components/events/video.rs @@ -0,0 +1,41 @@ +use matrix_sdk::events::room::message::MessageEvent; +use yew::prelude::*; + +struct Video { + props: Props, +} + +#[derive(Clone, Properties, Debug)] +pub struct Props { + #[prop_or_default] + pub prev_event: Option<MessageEvent>, + #[prop_or_default] + pub event: Option<MessageEvent>, +} + +impl Component for Video { + type Message = (); + type Properties = Props; + + fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self { + Video { props } + } + + fn update(&mut self, _msg: Self::Message) -> bool { + false + } + + fn change(&mut self, props: Self::Properties) -> bool { + // TODO fix the PartialEq hack + if format!("{:#?}", self.props) != format!("{:#?}", props) { + self.props = props; + true + } else { + false + } + } + + fn view(&self) -> Html { + html! {} + } +} diff --git a/src/app/components/mod.rs b/src/app/components/mod.rs @@ -1,2 +1,3 @@ pub mod event_list; +pub mod events; pub mod room_list; diff --git a/src/app/components/room_list.rs b/src/app/components/room_list.rs @@ -1,6 +1,5 @@ use std::collections::HashMap; -use log::*; use matrix_sdk::{identifiers::RoomId, js_int::UInt, Room}; use serde::{Deserialize, Serialize}; use wasm_bindgen::JsCast; @@ -131,7 +130,7 @@ impl Component for RoomList { //noinspection RsTypeCheck fn view(&self) -> Html { - return if self.state.loading { + if self.state.loading { html! { <div class="container"> <div class="uk-position-center uk-padding"> @@ -181,7 +180,7 @@ impl Component for RoomList { </div> </div> } - }; + } } } diff --git a/src/app/matrix.rs b/src/app/matrix.rs @@ -1,8 +1,3 @@ -use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; -use std::sync::{Arc, Mutex}; - -use futures_locks::RwLock; use log::*; use matrix_sdk::{ api::r0::{filter::RoomEventFilter, message::get_message_events::Direction}, @@ -13,9 +8,13 @@ use matrix_sdk::{ }, identifiers::RoomId, js_int::UInt, + locks::RwLock, Client, ClientConfig, MessagesRequestBuilder, Room, Session, }; use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; +use std::convert::TryFrom; +use std::sync::{Arc, Mutex}; use url::Url; use wasm_bindgen_futures::spawn_local; use yew::format::Json; @@ -168,6 +167,7 @@ impl Agent for MatrixAgent { }; client.restore_login(session).await; } else { + // FIXME gracefully handle login errors let login_response: matrix_sdk::api::r0::session::login::Response = client .login(username, password, None, Some("Daydream".to_string())) .await @@ -247,8 +247,8 @@ impl Agent for MatrixAgent { spawn_local(async move { let mut builder = &mut MessagesRequestBuilder::new(); builder = builder.room_id(room_id.clone()); - if from.is_some() { - builder = builder.from(from.unwrap()); + if let Some(from) = from { + builder = builder.from(from); } else { builder = builder.from( agent @@ -332,7 +332,7 @@ impl Agent for MatrixAgent { .get_joined_room(&room_id) .await .unwrap(); - let read_clone = room.clone().read().await; + let read_clone = room.read().await; let clean_room = (*read_clone).clone(); for sub in agent.subscribers.iter() { let resp = Response::JoinedRoom((room_id.clone(), clean_room.clone())); @@ -353,18 +353,16 @@ impl Agent for MatrixAgent { formatted_message = formatted_message.replace("<p>", "").replace("</p>", ""); formatted_message.pop(); - let content; - if formatted_message == message { - content = - MessageEventContent::Text(TextMessageEventContent::new_plain(message)); + let content = if formatted_message == message { + MessageEventContent::Text(TextMessageEventContent::new_plain(message)) } else { - content = MessageEventContent::Text(TextMessageEventContent { - body: message.into(), + MessageEventContent::Text(TextMessageEventContent { + body: message, format: Some("org.matrix.custom.html".to_string()), formatted_body: Some(formatted_message), relates_to: None, - }); - } + }) + }; client.room_send(&room_id, content, None).await; }); } @@ -397,7 +395,7 @@ impl MatrixAgent { } fn login(&mut self) -> Option<Client> { - return if (self.matrix_state.homeserver.is_none() + if (self.matrix_state.homeserver.is_none() || self.matrix_state.username.is_none() || self.matrix_state.password.is_none() || self.matrix_client.is_some()) @@ -412,7 +410,7 @@ impl MatrixAgent { let homeserver = self.session.clone().unwrap().homeserver_url; let client_config = ClientConfig::new(); - let homeserver_url = Url::parse(&homeserver.clone()).unwrap(); + let homeserver_url = Url::parse(&homeserver).unwrap(); let client = Client::new_with_config(homeserver_url, client_config).unwrap(); self.matrix_client = Some(client.clone()); @@ -429,16 +427,16 @@ impl MatrixAgent { client_clone.restore_login(session).await; }); - Some(client.clone()) + Some(client) } else { let homeserver = self.matrix_state.homeserver.clone().unwrap(); let client_config = ClientConfig::new(); - let homeserver_url = Url::parse(&homeserver.clone()).unwrap(); + let homeserver_url = Url::parse(&homeserver).unwrap(); let client = Client::new_with_config(homeserver_url, client_config).unwrap(); self.matrix_client = Some(client.clone()); - Some(client.clone()) - }; + Some(client) + } } } diff --git a/src/app/matrix/sync.rs b/src/app/matrix/sync.rs @@ -1,14 +1,15 @@ use std::time::Duration; -use log::*; use matrix_sdk::{ - api::r0::sync::sync_events::Response as SyncResponse, events::{collections::all::RoomEvent, room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},}, - identifiers::RoomId, Client, SyncSettings, + api::r0::sync::sync_events::Response as SyncResponse, + events::{collections::all::RoomEvent, room::message::MessageEventContent}, + identifiers::RoomId, + Client, SyncSettings, }; use yew::Callback; -use crate::app::matrix::Response; use crate::app::matrix::types::get_media_download_url; +use crate::app::matrix::Response; pub struct Sync { pub(crate) matrix_client: Client, diff --git a/src/app/matrix/types.rs b/src/app/matrix/types.rs @@ -2,9 +2,9 @@ use matrix_sdk::Client; pub fn get_media_download_url(client: Client, mxc_url: String) -> String { let url_parts_raw = mxc_url.replace("mxc://", ""); - let url_parts: Vec<&str> = url_parts_raw.split("/").collect(); - let server_name = url_parts.first().unwrap().to_string(); - let media_id = url_parts.last().unwrap().to_string(); + let url_parts: Vec<&str> = url_parts_raw.split('/').collect(); + let server_name = (*url_parts.first().unwrap()).to_string(); + let media_id = (*url_parts.last().unwrap()).to_string(); let new_path = format!( "_matrix/media/r0/download/{}/{}/fix.jpg", server_name, media_id, diff --git a/src/app/views/login.rs b/src/app/views/login.rs @@ -1,4 +1,3 @@ -use log::*; use yew::agent::{Dispatched, Dispatcher}; use yew::prelude::*; diff --git a/src/app/views/main_view.rs b/src/app/views/main_view.rs @@ -1,4 +1,3 @@ -use log::*; use matrix_sdk::Room; use serde::{Deserialize, Serialize}; use yew::prelude::*; @@ -52,7 +51,7 @@ impl Component for MainView { //noinspection RsTypeCheck fn view(&self) -> Html { - return if self.state.current_room.is_none() { + if self.state.current_room.is_none() { html! { <div class="uk-flex auto-scrollable-container uk-background-default" style="height: 100%"> <RoomList change_room_callback=self.link.callback(Msg::ChangeRoom)/> @@ -71,6 +70,6 @@ impl Component for MainView { <EventList current_room=self.state.current_room.as_ref().unwrap() /> </div> } - }; + } } }