commit 774306eb22d4e2e14e42735183651e5e3b249c47
parent a6305433d3982b40467586ca7f53031af1318f83
Author: Marcel <mtrnord1@gmail.com>
Date: Tue, 16 Jun 2020 18:39:28 +0200
Add basic notifications
Took 1 hour 19 minutes
Diffstat:
11 files changed, 148 insertions(+), 19 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -48,7 +48,11 @@ version = "0.3"
features = [
'KeyboardEvent',
'HtmlElement',
- 'DomStringMap'
+ 'DomStringMap',
+ 'Notification',
+ 'NotificationPermission',
+ 'NotificationOptions',
+ 'Window'
]
[patch.crates-io]
diff --git a/src/app/components/event_list.rs b/src/app/components/event_list.rs
@@ -7,8 +7,11 @@ use matrix_sdk::{
use std::collections::HashMap;
use yew::prelude::*;
-use crate::app::components::events::{image::Image, notice::Notice, text::Text, video::Video};
+use crate::app::components::events::{
+ get_sender_avatar, 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/image.rs b/src/app/components/events/image.rs
@@ -3,8 +3,8 @@ use matrix_sdk::{
events::room::message::{ImageMessageEventContent, MessageEvent},
Room,
};
-use yew::prelude::*;
use rand::random;
+use yew::prelude::*;
pub(crate) struct Image {
props: Props,
diff --git a/src/app/components/events/mod.rs b/src/app/components/events/mod.rs
@@ -1,5 +1,7 @@
+use crate::app::matrix::types::get_media_download_url;
use matrix_sdk::events::room::message::MessageEvent;
use matrix_sdk::Room;
+use url::Url;
pub mod image;
pub mod notice;
@@ -24,3 +26,16 @@ pub fn get_sender_displayname(room: Room, event: MessageEvent) -> String {
.unwrap_or_else(|| event.sender.to_string()),
}
}
+
+pub fn get_sender_avatar(homeserver_url: Url, room: Room, event: MessageEvent) -> Option<String> {
+ match room.members.get(&event.sender) {
+ None => None,
+ Some(member) => {
+ let avatar_url_mxc = member.avatar_url.as_ref().map(ToString::to_string);
+ match avatar_url_mxc {
+ None => None,
+ Some(v) => Some(get_media_download_url(&homeserver_url, v)),
+ }
+ }
+ }
+}
diff --git a/src/app/components/events/video.rs b/src/app/components/events/video.rs
@@ -3,8 +3,8 @@ use matrix_sdk::{
events::room::message::{MessageEvent, VideoMessageEventContent},
Room,
};
-use yew::prelude::*;
use rand::random;
+use yew::prelude::*;
pub(crate) struct Video {
props: Props,
diff --git a/src/app/matrix/mod.rs b/src/app/matrix/mod.rs
@@ -334,13 +334,14 @@ impl Agent for MatrixAgent {
oks.into_iter().map(Result::unwrap).collect();
for event in deserialized_events.into_iter().rev() {
+ // TODO deduplicate betweeen this and sync
if let RoomEvent::RoomMessage(mut event) = event {
if let MessageEventContent::Image(mut image_event) =
event.clone().content
{
if image_event.url.is_some() {
let new_url = Some(get_media_download_url(
- agent.matrix_client.clone().unwrap(),
+ agent.matrix_client.clone().unwrap().homeserver(),
image_event.url.unwrap(),
));
image_event.url = new_url;
@@ -349,7 +350,7 @@ impl Agent for MatrixAgent {
let mut info = image_event.info.unwrap();
if info.thumbnail_url.is_some() {
let new_url = Some(get_media_download_url(
- agent.matrix_client.clone().unwrap(),
+ agent.matrix_client.clone().unwrap().homeserver(),
info.thumbnail_url.unwrap(),
));
info.thumbnail_url = new_url;
@@ -361,7 +362,7 @@ impl Agent for MatrixAgent {
if let MessageEventContent::Video(mut video_event) = event.content {
if video_event.url.is_some() {
let new_url = Some(get_video_media_download_url(
- agent.matrix_client.clone().unwrap(),
+ agent.matrix_client.clone().unwrap().homeserver(),
video_event.url.unwrap(),
));
video_event.url = new_url;
@@ -370,7 +371,7 @@ impl Agent for MatrixAgent {
let mut info = video_event.info.unwrap();
if info.thumbnail_url.is_some() {
let new_url = Some(get_media_download_url(
- agent.matrix_client.clone().unwrap(),
+ agent.matrix_client.clone().unwrap().homeserver(),
info.thumbnail_url.unwrap(),
));
info.thumbnail_url = new_url;
diff --git a/src/app/matrix/sync.rs b/src/app/matrix/sync.rs
@@ -1,16 +1,22 @@
+use std::sync::Arc;
use std::time::Duration;
-use crate::app::matrix::types::{get_media_download_url, get_video_media_download_url};
-use crate::app::matrix::Response;
use log::*;
use matrix_sdk::{
api::r0::sync::sync_events::Response as SyncResponse,
events::{collections::all::RoomEvent, room::message::MessageEventContent},
identifiers::RoomId,
- Client, SyncSettings,
+ locks::RwLock,
+ Client, Room, SyncSettings,
};
+use wasm_bindgen_futures::spawn_local;
use yew::Callback;
+use crate::app::components::events::{get_sender_avatar, get_sender_displayname};
+use crate::app::matrix::types::{get_media_download_url, get_video_media_download_url};
+use crate::app::matrix::Response;
+use crate::utils::notifications::Notifications;
+
pub struct Sync {
pub(crate) matrix_client: Client,
pub(crate) callback: Callback<Response>,
@@ -47,10 +53,31 @@ impl Sync {
// TODO handle all messages...
if let RoomEvent::RoomMessage(mut event) = event {
+ if let MessageEventContent::Text(text_event) = event.clone().content {
+ let homeserver_url = self.matrix_client.clone().homeserver().clone();
+
+ let cloned_event = event.clone();
+ 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 let MessageEventContent::Image(mut image_event) = event.clone().content {
if image_event.url.is_some() {
let new_url = Some(get_media_download_url(
- self.matrix_client.clone(),
+ self.matrix_client.clone().homeserver(),
image_event.url.unwrap(),
));
image_event.url = new_url;
@@ -59,7 +86,7 @@ impl Sync {
let mut info = image_event.info.unwrap();
if info.thumbnail_url.is_some() {
let new_url = Some(get_media_download_url(
- self.matrix_client.clone(),
+ self.matrix_client.clone().homeserver(),
info.thumbnail_url.unwrap(),
));
info.thumbnail_url = new_url;
@@ -71,7 +98,7 @@ impl Sync {
if let MessageEventContent::Video(mut video_event) = event.content {
if video_event.url.is_some() {
let new_url = Some(get_video_media_download_url(
- self.matrix_client.clone(),
+ self.matrix_client.clone().homeserver(),
video_event.url.unwrap(),
));
video_event.url = new_url;
@@ -80,7 +107,7 @@ impl Sync {
let mut info = video_event.info.unwrap();
if info.thumbnail_url.is_some() {
let new_url = Some(get_media_download_url(
- self.matrix_client.clone(),
+ self.matrix_client.clone().homeserver(),
info.thumbnail_url.unwrap(),
));
info.thumbnail_url = new_url;
@@ -89,6 +116,7 @@ impl Sync {
}
event.content = MessageEventContent::Video(video_event);
}
+
let resp = Response::Sync((room_id.clone(), event.clone()));
self.callback.emit(resp);
}
diff --git a/src/app/matrix/types.rs b/src/app/matrix/types.rs
@@ -1,6 +1,7 @@
use matrix_sdk::Client;
+use url::Url;
-pub fn get_media_download_url(client: Client, mxc_url: String) -> String {
+pub fn get_media_download_url(homeserver: &Url, 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();
@@ -9,12 +10,12 @@ pub fn get_media_download_url(client: Client, mxc_url: String) -> String {
"_matrix/media/r0/download/{}/{}/fix.jpg",
server_name, media_id,
);
- let mut new_url = client.homeserver().clone();
+ let mut new_url = homeserver.clone();
new_url.set_path(new_path.as_str());
new_url.to_string()
}
-pub fn get_video_media_download_url(client: Client, mxc_url: String) -> String {
+pub fn get_video_media_download_url(homeserver: &Url, 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();
@@ -23,7 +24,7 @@ pub fn get_video_media_download_url(client: Client, mxc_url: String) -> String {
"_matrix/media/r0/download/{}/{}/fix.mp4",
server_name, media_id,
);
- let mut new_url = client.homeserver().clone();
+ let mut new_url = homeserver.clone();
new_url.set_path(new_path.as_str());
new_url.to_string()
}
diff --git a/src/lib.rs b/src/lib.rs
@@ -10,6 +10,7 @@ use wasm_bindgen::prelude::*;
pub mod app;
mod constants;
mod errors;
+pub mod utils;
#[derive(RustEmbed, I18nEmbed)]
#[folder = "i18n/mo"] // path to the compiled localization resources
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
@@ -0,0 +1 @@
+pub mod notifications;
diff --git a/src/utils/notifications.rs b/src/utils/notifications.rs
@@ -0,0 +1,75 @@
+use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsCast;
+use web_sys::{window, Notification, NotificationOptions, NotificationPermission, Window};
+
+#[derive(Clone)]
+pub(crate) struct Notifications {
+ window: Option<Window>,
+ avatar: Option<String>,
+ displayname: String,
+ content: String,
+}
+
+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,
+ },
+ Some(v) => match v.get("Notification") {
+ Some(_) => true,
+ _ => false,
+ },
+ };
+ }
+
+ fn notifications_allowed(&self) -> bool {
+ match Notification::permission() {
+ NotificationPermission::Granted => true,
+ _ => false,
+ }
+ }
+
+ pub fn show(&self) {
+ if !self.notifications_allowed() {
+ let self_clone = self.clone();
+ let cb = Closure::wrap(Box::new(move || {
+ if self_clone.notifications_allowed() {
+ self_clone.clone().show_actual();
+ }
+ }) as Box<dyn FnMut()>);
+
+ Notification::request_permission_with_permission_callback(cb.as_ref().unchecked_ref());
+ cb.forget();
+ } else {
+ self.show_actual();
+ }
+ }
+
+ 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 = if self.avatar.is_some() {
+ options.icon(self.avatar.as_ref().unwrap())
+ } else {
+ options
+ };
+ Notification::new_with_options(&self.displayname, &options);
+ }
+}