commit d9c8875497732c06ecc1f25d84ec760c742d266d
parent 651ad3ece8adfc3bebbe03d695f87b89cff169fe
Author: Marcel <mtrnord1@gmail.com>
Date: Sat, 6 Jun 2020 20:01:00 +0200
Split all current events to own Components, Add Video Event, Prepare translations
Took 40 minutes
Diffstat:
33 files changed, 1051 insertions(+), 500 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -22,13 +22,20 @@ wee_alloc = "0.4"
yew = { git = "https://github.com/yewstack/yew" }
yew-router = { git = "https://github.com/yewstack/yew" }
yewtil = { git = "https://github.com/yewstack/yew" }
+
+# Matrix
matrix-sdk = { version = "0.1.0", git = "https://github.com/MTRNord/matrix-rust-sdk", branch = "expose-send-rebased", default-features = false}# features = ["encryption"]}
url = "2.1.1"
thiserror = "1.0"
futures-locks = { git = "https://github.com/asomers/futures-locks", default-features = false }
-linked_hash_set = {version = "0.1.3", features = ["serde"] }
+
+# Markdown
pulldown-cmark = "0.7.1"
+# Translations
+tr = { version = "0.1.3", default-features = false, features = ["gettext"]}
+i18n-embed = { version = "0.4", features = ["web-sys-requester"]}
+
[dev-dependencies]
wasm-bindgen-test = "0.3"
diff --git a/README.md b/README.md
@@ -80,6 +80,10 @@ This is an example of how to list things you need to use the software and how to
* Rust
* Nodejs
* Yarn
+* gettext (See: https://github.com/kellpossible/cargo-i18n#system-requirements)
+* When you want to recompile translations:
+ * https://github.com/kellpossible/cargo-i18n
+ * `cargo install xtr`
### Setting up a Development Environment
diff --git a/i18n.toml b/i18n.toml
@@ -0,0 +1,11 @@
+# (Required) The locale/language identifier of the language used in the source
+# code.
+src_locale = "en"
+
+# (Required) The locales that the software will be translated into.
+target_locales = ["de"]
+
+[gettext]
+# (Required) Path to the output directory, relative to `i18n.toml` of the crate
+# being localized.
+output_dir = "i18n"
diff --git a/i18n/po/de/daydream.po b/i18n/po/de/daydream.po
diff --git a/i18n/pot/daydream.pot b/i18n/pot/daydream.pot
diff --git a/i18n/pot/src/app/components/event_list.pot b/i18n/pot/src/app/components/event_list.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/events/image.pot b/i18n/pot/src/app/components/events/image.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/events/mod.pot b/i18n/pot/src/app/components/events/mod.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/events/notice.pot b/i18n/pot/src/app/components/events/notice.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/events/text.pot b/i18n/pot/src/app/components/events/text.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/events/video.pot b/i18n/pot/src/app/components/events/video.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/mod.pot b/i18n/pot/src/app/components/mod.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/components/room_list.pot b/i18n/pot/src/app/components/room_list.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/matrix/mod.pot b/i18n/pot/src/app/matrix/mod.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/matrix/sync.pot b/i18n/pot/src/app/matrix/sync.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/matrix/types.pot b/i18n/pot/src/app/matrix/types.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/mod.pot b/i18n/pot/src/app/mod.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/views/login.pot b/i18n/pot/src/app/views/login.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/views/main_view.pot b/i18n/pot/src/app/views/main_view.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/app/views/mod.pot b/i18n/pot/src/app/views/mod.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/constants.pot b/i18n/pot/src/constants.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/errors.pot b/i18n/pot/src/errors.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/i18n/pot/src/lib.pot b/i18n/pot/src/lib.pot
@@ -0,0 +1,18 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE daydream'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the daydream package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: daydream 0.1.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2020-06-06 17:49+0000\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
diff --git a/src/app/components/event_list.rs b/src/app/components/event_list.rs
@@ -8,7 +8,7 @@ use matrix_sdk::{
};
use yew::prelude::*;
-use crate::app::components::events::text::Text;
+use crate::app::components::events::{text::Text, notice::Notice, image::Image, video::Video};
use crate::app::matrix::{MatrixAgent, Request, Response};
pub struct EventList {
@@ -224,46 +224,33 @@ impl EventList {
}
}
MessageEventContent::Notice(notice_event) => {
- if new_user {
- html! {
- <p style="opacity: .6;"><displayname>{sender_displayname}{": "}</displayname>{notice_event.body.clone()}</p>
- }
- } else {
- html! {
- <p style="opacity: .6;">{notice_event.body.clone()}</p>
- }
+ html! {
+ <Notice
+ prev_event=prev_event.clone()
+ event=Some(event.clone())
+ room=Some(self.props.current_room.clone().unwrap())
+ notice_event=Some(notice_event.clone())
+ />
}
}
MessageEventContent::Image(image_event) => {
- let caption = format!("{}: {}", sender_displayname, image_event.body);
- if image_event.url.clone().is_some() {
- let image_url = image_event.url.clone().unwrap();
- let thumbnail = match image_event.info.clone().unwrap().thumbnail_url {
- None => image_url.clone(),
- Some(v) => v,
- };
- if new_user {
- html! {
- <>
- <p><displayname>{sender_displayname}{": "}</displayname></p>
- <div uk-lightbox="">
- <a class="uk-inline" href=image_url data-caption=caption >
- <img src=thumbnail alt=caption />
- </a>
- </div>
- </>
- }
- } else {
- html! {
- <div uk-lightbox="">
- <a class="uk-inline" href=image_url data-caption=caption >
- <img src=thumbnail alt=caption />
- </a>
- </div>
- }
- }
- } else {
- html! {}
+ html! {
+ <Image
+ prev_event=prev_event.clone()
+ event=Some(event.clone())
+ room=Some(self.props.current_room.clone().unwrap())
+ image_event=Some(image_event.clone())
+ />
+ }
+ }
+ MessageEventContent::Video(video_event) => {
+ html! {
+ <Video
+ prev_event=prev_event.clone()
+ event=Some(event.clone())
+ room=Some(self.props.current_room.clone().unwrap())
+ video_event=Some(video_event.clone())
+ />
}
}
_ => {
diff --git a/src/app/components/events/image.rs b/src/app/components/events/image.rs
@@ -1,7 +1,11 @@
-use matrix_sdk::events::room::message::MessageEvent;
+use matrix_sdk::{
+ events::room::message::{MessageEvent, ImageMessageEventContent},
+ Room,
+};
use yew::prelude::*;
+use crate::app::components::events::{is_new_user, get_sender_displayname};
-struct Image {
+pub(crate) struct Image {
props: Props,
}
@@ -11,6 +15,10 @@ pub struct Props {
pub prev_event: Option<MessageEvent>,
#[prop_or_default]
pub event: Option<MessageEvent>,
+ #[prop_or_default]
+ pub image_event: Option<ImageMessageEventContent>,
+ #[prop_or_default]
+ pub room: Option<Room>,
}
impl Component for Image {
@@ -35,7 +43,51 @@ impl Component for Image {
}
}
+ //noinspection RsTypeCheck
fn view(&self) -> Html {
- 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()
+ };
+
+
+ let caption = format!("{}: {}", sender_displayname, self.props.image_event.as_ref().unwrap().body);
+ if self.props.image_event.as_ref().unwrap().url.clone().is_some() {
+ let image_url = self.props.image_event.as_ref().unwrap().url.clone().unwrap();
+ let thumbnail = match self.props.image_event.as_ref().unwrap().info.clone().unwrap().thumbnail_url {
+ None => image_url.clone(),
+ Some(v) => v,
+ };
+ if new_user {
+ html! {
+ <>
+ <p><displayname>{sender_displayname}{": "}</displayname></p>
+ <div uk-lightbox="">
+ <a class="uk-inline" href=image_url data-caption=caption >
+ <img src=thumbnail alt=caption />
+ </a>
+ </div>
+ </>
+ }
+ } else {
+ html! {
+ <div uk-lightbox="">
+ <a class="uk-inline" href=image_url data-caption=caption >
+ <img src=thumbnail alt=caption />
+ </a>
+ </div>
+ }
+ }
+ } else {
+ html! {}
+ }
}
}
diff --git a/src/app/components/events/notice.rs b/src/app/components/events/notice.rs
@@ -1,7 +1,12 @@
-use matrix_sdk::events::room::message::MessageEvent;
+use matrix_sdk::{
+ events::room::message::{MessageEvent, NoticeMessageEventContent},
+ Room,
+};
use yew::prelude::*;
-struct Notice {
+use crate::app::components::events::{get_sender_displayname, is_new_user};
+
+pub(crate) struct Notice {
props: Props,
}
@@ -11,6 +16,10 @@ pub struct Props {
pub prev_event: Option<MessageEvent>,
#[prop_or_default]
pub event: Option<MessageEvent>,
+ #[prop_or_default]
+ pub notice_event: Option<NoticeMessageEventContent>,
+ #[prop_or_default]
+ pub room: Option<Room>,
}
impl Component for Notice {
@@ -35,7 +44,43 @@ impl Component for Notice {
}
}
+ //noinspection RsTypeCheck
fn view(&self) -> Html {
- 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 new_user {
+ html! {
+ <p style="opacity: .6;"><displayname>{sender_displayname}{": "}</displayname>
+ {
+ self.props
+ .notice_event
+ .clone()
+ .unwrap().body.clone()
+ }
+ </p>
+ }
+ } else {
+ html! {
+ <p style="opacity: .6;">
+ {
+ self.props
+ .notice_event
+ .clone()
+ .unwrap().body.clone()
+ }
+ </p>
+ }
+ }
}
}
diff --git a/src/app/components/events/video.rs b/src/app/components/events/video.rs
@@ -1,7 +1,11 @@
-use matrix_sdk::events::room::message::MessageEvent;
+use matrix_sdk::{
+ events::room::message::{MessageEvent, VideoMessageEventContent},
+ Room,
+};
use yew::prelude::*;
+use crate::app::components::events::{is_new_user, get_sender_displayname};
-struct Video {
+pub(crate) struct Video {
props: Props,
}
@@ -11,6 +15,10 @@ pub struct Props {
pub prev_event: Option<MessageEvent>,
#[prop_or_default]
pub event: Option<MessageEvent>,
+ #[prop_or_default]
+ pub video_event: Option<VideoMessageEventContent>,
+ #[prop_or_default]
+ pub room: Option<Room>,
}
impl Component for Video {
@@ -35,7 +43,51 @@ impl Component for Video {
}
}
+ //noinspection RsTypeCheck
fn view(&self) -> Html {
- 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()
+ };
+
+
+ let caption = format!("{}: {}", sender_displayname, self.props.video_event.as_ref().unwrap().body);
+ if self.props.video_event.as_ref().unwrap().url.clone().is_some() {
+ let video_url = self.props.video_event.as_ref().unwrap().url.clone().unwrap();
+ let thumbnail = match self.props.video_event.as_ref().unwrap().info.clone().unwrap().thumbnail_url {
+ None => video_url.clone(),
+ Some(v) => v,
+ };
+ if new_user {
+ html! {
+ <>
+ <p><displayname>{sender_displayname}{": "}</displayname></p>
+ <div uk-lightbox="">
+ <a class="uk-inline" href=video_url data-caption=caption data-poster=thumbnail >
+ <img src=thumbnail alt=caption />
+ </a>
+ </div>
+ </>
+ }
+ } else {
+ html! {
+ <div uk-lightbox="">
+ <a class="uk-inline" href=video_url data-caption=caption data-poster=thumbnail >
+ <img src=thumbnail alt=caption />
+ </a>
+ </div>
+ }
+ }
+ } else {
+ html! {}
+ }
}
}
diff --git a/src/app/matrix.rs b/src/app/matrix.rs
@@ -1,445 +0,0 @@
-use log::*;
-use matrix_sdk::{
- api::r0::{filter::RoomEventFilter, message::get_message_events::Direction},
- events::{
- collections::all::RoomEvent,
- room::message::{MessageEvent, MessageEventContent, TextMessageEventContent, FormattedBody, MessageFormat},
- EventJson,
- },
- 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;
-use yew::services::{storage::Area, StorageService};
-use yew::worker::*;
-
-use crate::app::matrix::types::get_media_download_url;
-use crate::constants::AUTH_KEY;
-use crate::errors::MatrixError;
-use pulldown_cmark::{html, Options, Parser};
-
-mod sync;
-pub mod types;
-
-#[derive(Serialize, Deserialize, Default, Clone, Debug)]
-pub struct MatrixClient {
- pub(crate) homeserver: Option<String>,
- pub(crate) username: Option<String>,
- pub(crate) password: Option<String>,
-}
-
-#[derive(Serialize, Deserialize, Default, Clone, Debug)]
-pub struct SessionStore {
- pub(crate) access_token: String,
- pub(crate) user_id: String,
- pub(crate) device_id: String,
- pub(crate) homeserver_url: String,
-}
-
-#[derive(Clone, Debug)]
-pub struct MatrixAgent {
- link: AgentLink<MatrixAgent>,
- matrix_state: MatrixClient,
- matrix_client: Option<Client>,
- // TODO make arc mutex :(
- subscribers: HashSet<HandlerId>,
- storage: Arc<Mutex<StorageService>>,
- session: Option<SessionStore>,
-}
-
-#[derive(Serialize, Deserialize, Debug)]
-pub enum Request {
- SetHomeserver(String),
- SetUsername(String),
- SetPassword(String),
- Login(),
- GetLoggedIn,
- GetUserdata,
- GetOldMessages((RoomId, Option<String>)),
- StartSync,
- GetJoinedRooms,
- GetJoinedRoom(RoomId),
- SendMessage((RoomId, String)),
-}
-
-#[allow(clippy::large_enum_variant)]
-#[derive(Debug, Clone)]
-pub enum Response {
- Error(MatrixError),
- LoggedIn(bool),
- // TODO properly handle sync events
- Sync((RoomId, MessageEvent)),
- SyncPing,
- JoinedRoomList(HashMap<RoomId, Room>),
- Userdata(),
- OldMessages((RoomId, Vec<MessageEvent>)),
- JoinedRoom((RoomId, Room)),
-}
-
-#[derive(Debug, Clone)]
-pub enum Msg {
- OnSyncResponse(Response),
-}
-
-impl Agent for MatrixAgent {
- type Reach = Context<MatrixAgent>;
- type Message = Msg;
- type Input = Request;
- type Output = Response;
-
- fn create(link: AgentLink<Self>) -> Self {
- let storage = Arc::new(Mutex::new(
- StorageService::new(Area::Local).expect("storage was disabled by the user"),
- ));
- let session: Option<SessionStore> = {
- if let Json(Ok(restored_model)) = storage.lock().unwrap().restore(AUTH_KEY) {
- Some(restored_model)
- } else {
- None
- }
- };
- MatrixAgent {
- link,
- matrix_state: Default::default(),
- matrix_client: None,
- subscribers: HashSet::new(),
- storage,
- session,
- }
- }
-
- fn update(&mut self, msg: Self::Message) {
- match msg {
- Msg::OnSyncResponse(resp) => {
- for sub in self.subscribers.iter() {
- self.link.respond(*sub, resp.clone());
- }
- }
- }
- }
-
- fn connected(&mut self, id: HandlerId) {
- self.subscribers.insert(id);
- }
- fn handle_input(&mut self, msg: Self::Input, _: HandlerId) {
- match msg {
- Request::SetHomeserver(homeserver) => {
- self.matrix_state.homeserver = Some(homeserver);
- }
- Request::SetUsername(username) => {
- self.matrix_state.username = Some(username);
- }
- Request::SetPassword(password) => {
- self.matrix_state.password = Some(password);
- }
- Request::Login() => {
- let login_client = self.login();
- if login_client.is_none() {
- for sub in self.subscribers.iter() {
- let resp = Response::Error(MatrixError::MissingClient);
- self.link.respond(*sub, resp);
- }
- return;
- }
- let client = login_client.unwrap();
- let username = self.matrix_state.username.clone().unwrap();
- let password = self.matrix_state.password.clone().unwrap();
- let subscribers = self.subscribers.clone();
- let agent = self.clone();
- spawn_local(async move {
- // TODO handle login error
- if agent.session.is_some() {
- let stored_session = agent.session.unwrap();
- let session = Session {
- access_token: stored_session.access_token,
- user_id: matrix_sdk::identifiers::UserId::try_from(
- stored_session.user_id.as_str(),
- )
- .unwrap(),
- device_id: stored_session.device_id,
- };
- 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
- .unwrap();
- let session_store = SessionStore {
- access_token: login_response.access_token,
- user_id: login_response.user_id.to_string(),
- device_id: login_response.device_id,
- homeserver_url: client.homeserver().to_string(),
- };
- let mut storage = agent.storage.lock().unwrap();
- storage.store(AUTH_KEY, Json(&session_store));
- }
-
- for sub in subscribers.iter() {
- let resp = Response::LoggedIn(true);
- agent.link.respond(*sub, resp);
- }
- });
- }
- Request::GetLoggedIn => {
- let login_client = self.login();
- if login_client.is_none() {
- for sub in self.subscribers.iter() {
- let resp = Response::Error(MatrixError::MissingClient);
- self.link.respond(*sub, resp);
- }
- return;
- }
-
- // Always clone agent after having tried to login!
- let agent = self.clone();
-
- spawn_local(async move {
- let logged_in = agent.get_logged_in().await;
-
- if !logged_in && agent.session.is_some() {
- error!("Not logged in but got session");
- } else {
- for sub in agent.subscribers.iter() {
- let resp = Response::LoggedIn(logged_in);
- agent.link.respond(*sub, resp);
- }
- }
- });
- }
- Request::StartSync => {
- // Always clone agent after having tried to login!
- let agent = self.clone();
- spawn_local(async move {
- agent.start_sync().await;
- });
- }
- Request::GetJoinedRooms => {
- let agent = self.clone();
- spawn_local(async move {
- let rooms: Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> =
- agent.matrix_client.unwrap().joined_rooms();
-
- let readable_rooms = rooms.read().await;
- let mut rooms_unarced: HashMap<RoomId, Room> = HashMap::new();
- for (id, room) in readable_rooms.iter() {
- let unarced_room = (*room.write().await).clone();
- rooms_unarced.insert(id.clone(), unarced_room);
- }
- for sub in agent.subscribers.iter() {
- let resp = Response::JoinedRoomList(rooms_unarced.clone());
- agent.link.respond(*sub, resp);
- }
- });
- }
- Request::GetUserdata => {
- // Noop
- }
- Request::GetOldMessages((room_id, from)) => {
- let agent = self.clone();
- spawn_local(async move {
- let mut builder = &mut MessagesRequestBuilder::new();
- builder = builder.room_id(room_id.clone());
- if let Some(from) = from {
- builder = builder.from(from);
- } else {
- builder = builder.from(
- agent
- .matrix_client
- .clone()
- .unwrap()
- .sync_token()
- .await
- .unwrap(),
- );
- }
- let filter = RoomEventFilter {
- types: Some(vec!["m.room.message".to_string()]),
- ..Default::default()
- };
- builder = builder
- .filter(filter)
- .direction(Direction::Backward)
- .limit(UInt::new(30).unwrap());
-
- // TODO handle error gracefully
- let messsages = agent
- .matrix_client
- .clone()
- .unwrap()
- .room_messages(builder.clone())
- .await
- .unwrap();
- // TODO save end point for future loading
-
- let mut wrapped_messages: Vec<MessageEvent> = Vec::new();
- let chunk_iter: Vec<EventJson<RoomEvent>> = messsages.chunk;
- let (oks, _): (Vec<_>, Vec<_>) = chunk_iter
- .iter()
- .map(|event| event.deserialize())
- .partition(Result::is_ok);
-
- let deserialized_events: Vec<RoomEvent> =
- oks.into_iter().map(Result::unwrap).collect();
-
- for event in deserialized_events.into_iter().rev() {
- if let RoomEvent::RoomMessage(mut event) = event {
- if let MessageEventContent::Image(mut image_event) = event.content {
- if image_event.url.is_some() {
- let new_url = Some(get_media_download_url(
- agent.matrix_client.clone().unwrap(),
- image_event.url.unwrap(),
- ));
- image_event.url = new_url;
- }
- if image_event.info.is_some() {
- 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(),
- info.thumbnail_url.unwrap(),
- ));
- info.thumbnail_url = new_url;
- }
- image_event.info = Some(info);
- }
- event.content = MessageEventContent::Image(image_event);
- }
- wrapped_messages.push(event.clone());
- }
- }
-
- for sub in agent.subscribers.iter() {
- let resp =
- Response::OldMessages((room_id.clone(), wrapped_messages.clone()));
- agent.link.respond(*sub, resp);
- }
- });
- }
- Request::GetJoinedRoom(room_id) => {
- let agent = self.clone();
- spawn_local(async move {
- let room: Arc<RwLock<Room>> = agent
- .matrix_client
- .unwrap()
- .get_joined_room(&room_id)
- .await
- .unwrap();
- 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()));
- agent.link.respond(*sub, resp);
- }
- });
- }
- Request::SendMessage((room_id, message)) => {
- let client = self.matrix_client.clone().unwrap();
- spawn_local(async move {
- let mut options = Options::empty();
- options.insert(Options::ENABLE_STRIKETHROUGH);
- let parser = Parser::new_ext(message.as_str(), options);
-
- let mut formatted_message: String =
- String::with_capacity(message.len() * 3 / 2);
- html::push_html(&mut formatted_message, parser);
- formatted_message = formatted_message.replace("<p>", "").replace("</p>", "");
- formatted_message.pop();
-
- let content = if formatted_message == message {
- MessageEventContent::Text(TextMessageEventContent::new_plain(message))
- } else {
- MessageEventContent::Text(TextMessageEventContent {
- body: message,
- relates_to: None,
- formatted: Some(FormattedBody{
- body: formatted_message,
- format: MessageFormat::Html,
- })
- })
- };
- client.room_send(&room_id, content, None).await;
- });
- }
- }
- }
-
- fn disconnected(&mut self, id: HandlerId) {
- self.subscribers.remove(&id);
- }
-}
-
-unsafe impl Send for MatrixAgent {}
-
-unsafe impl std::marker::Sync for MatrixAgent {}
-
-impl MatrixAgent {
- async fn start_sync(&self) {
- let sync = sync::Sync {
- matrix_client: self.matrix_client.clone().unwrap(),
- callback: self.link.callback(Msg::OnSyncResponse),
- };
- sync.start_sync().await;
- }
-
- async fn get_logged_in(&self) -> bool {
- if self.matrix_client.is_none() {
- return false;
- }
- self.matrix_client.as_ref().unwrap().logged_in().await
- }
-
- fn login(&mut self) -> Option<Client> {
- if (self.matrix_state.homeserver.is_none()
- || self.matrix_state.username.is_none()
- || self.matrix_state.password.is_none()
- || self.matrix_client.is_some())
- && self.session.is_none()
- {
- for sub in self.subscribers.iter() {
- let resp = Response::Error(MatrixError::MissingFields);
- self.link.respond(*sub, resp);
- }
- None
- } else if self.session.is_some() {
- let homeserver = self.session.clone().unwrap().homeserver_url;
-
- let client_config = ClientConfig::new();
- let homeserver_url = Url::parse(&homeserver).unwrap();
- let client = Client::new_with_config(homeserver_url, client_config).unwrap();
- self.matrix_client = Some(client.clone());
-
- // Also directly restore Login data
- let stored_session = self.session.clone().unwrap();
- let session = Session {
- access_token: stored_session.access_token,
- user_id: matrix_sdk::identifiers::UserId::try_from(stored_session.user_id.as_str())
- .unwrap(),
- device_id: stored_session.device_id,
- };
- let client_clone = client.clone();
- spawn_local(async move {
- client_clone.restore_login(session).await;
- });
-
- Some(client)
- } else {
- let homeserver = self.matrix_state.homeserver.clone().unwrap();
-
- let client_config = ClientConfig::new();
- 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)
- }
- }
-}
diff --git a/src/app/matrix/mod.rs b/src/app/matrix/mod.rs
@@ -0,0 +1,466 @@
+use log::*;
+use matrix_sdk::{
+ api::r0::{filter::RoomEventFilter, message::get_message_events::Direction},
+ events::{
+ collections::all::RoomEvent,
+ room::message::{MessageEvent, MessageEventContent, TextMessageEventContent, FormattedBody, MessageFormat},
+ EventJson,
+ },
+ 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;
+use yew::services::{storage::Area, StorageService};
+use yew::worker::*;
+
+use crate::app::matrix::types::{get_media_download_url, get_video_media_download_url};
+use crate::constants::AUTH_KEY;
+use crate::errors::MatrixError;
+use pulldown_cmark::{html, Options, Parser};
+
+mod sync;
+pub mod types;
+
+#[derive(Serialize, Deserialize, Default, Clone, Debug)]
+pub struct MatrixClient {
+ pub(crate) homeserver: Option<String>,
+ pub(crate) username: Option<String>,
+ pub(crate) password: Option<String>,
+}
+
+#[derive(Serialize, Deserialize, Default, Clone, Debug)]
+pub struct SessionStore {
+ pub(crate) access_token: String,
+ pub(crate) user_id: String,
+ pub(crate) device_id: String,
+ pub(crate) homeserver_url: String,
+}
+
+#[derive(Clone, Debug)]
+pub struct MatrixAgent {
+ link: AgentLink<MatrixAgent>,
+ matrix_state: MatrixClient,
+ matrix_client: Option<Client>,
+ // TODO make arc mutex :(
+ subscribers: HashSet<HandlerId>,
+ storage: Arc<Mutex<StorageService>>,
+ session: Option<SessionStore>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub enum Request {
+ SetHomeserver(String),
+ SetUsername(String),
+ SetPassword(String),
+ Login(),
+ GetLoggedIn,
+ GetUserdata,
+ GetOldMessages((RoomId, Option<String>)),
+ StartSync,
+ GetJoinedRooms,
+ GetJoinedRoom(RoomId),
+ SendMessage((RoomId, String)),
+}
+
+#[allow(clippy::large_enum_variant)]
+#[derive(Debug, Clone)]
+pub enum Response {
+ Error(MatrixError),
+ LoggedIn(bool),
+ // TODO properly handle sync events
+ Sync((RoomId, MessageEvent)),
+ SyncPing,
+ JoinedRoomList(HashMap<RoomId, Room>),
+ Userdata(),
+ OldMessages((RoomId, Vec<MessageEvent>)),
+ JoinedRoom((RoomId, Room)),
+}
+
+#[derive(Debug, Clone)]
+pub enum Msg {
+ OnSyncResponse(Response),
+}
+
+impl Agent for MatrixAgent {
+ type Reach = Context<MatrixAgent>;
+ type Message = Msg;
+ type Input = Request;
+ type Output = Response;
+
+ fn create(link: AgentLink<Self>) -> Self {
+ let storage = Arc::new(Mutex::new(
+ StorageService::new(Area::Local).expect("storage was disabled by the user"),
+ ));
+ let session: Option<SessionStore> = {
+ if let Json(Ok(restored_model)) = storage.lock().unwrap().restore(AUTH_KEY) {
+ Some(restored_model)
+ } else {
+ None
+ }
+ };
+ MatrixAgent {
+ link,
+ matrix_state: Default::default(),
+ matrix_client: None,
+ subscribers: HashSet::new(),
+ storage,
+ session,
+ }
+ }
+
+ fn update(&mut self, msg: Self::Message) {
+ match msg {
+ Msg::OnSyncResponse(resp) => {
+ for sub in self.subscribers.iter() {
+ self.link.respond(*sub, resp.clone());
+ }
+ }
+ }
+ }
+
+ fn connected(&mut self, id: HandlerId) {
+ self.subscribers.insert(id);
+ }
+ fn handle_input(&mut self, msg: Self::Input, _: HandlerId) {
+ match msg {
+ Request::SetHomeserver(homeserver) => {
+ self.matrix_state.homeserver = Some(homeserver);
+ }
+ Request::SetUsername(username) => {
+ self.matrix_state.username = Some(username);
+ }
+ Request::SetPassword(password) => {
+ self.matrix_state.password = Some(password);
+ }
+ Request::Login() => {
+ let login_client = self.login();
+ if login_client.is_none() {
+ for sub in self.subscribers.iter() {
+ let resp = Response::Error(MatrixError::MissingClient);
+ self.link.respond(*sub, resp);
+ }
+ return;
+ }
+ let client = login_client.unwrap();
+ let username = self.matrix_state.username.clone().unwrap();
+ let password = self.matrix_state.password.clone().unwrap();
+ let subscribers = self.subscribers.clone();
+ let agent = self.clone();
+ spawn_local(async move {
+ // TODO handle login error
+ if agent.session.is_some() {
+ let stored_session = agent.session.unwrap();
+ let session = Session {
+ access_token: stored_session.access_token,
+ user_id: matrix_sdk::identifiers::UserId::try_from(
+ stored_session.user_id.as_str(),
+ )
+ .unwrap(),
+ device_id: stored_session.device_id,
+ };
+ 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
+ .unwrap();
+ let session_store = SessionStore {
+ access_token: login_response.access_token,
+ user_id: login_response.user_id.to_string(),
+ device_id: login_response.device_id,
+ homeserver_url: client.homeserver().to_string(),
+ };
+ let mut storage = agent.storage.lock().unwrap();
+ storage.store(AUTH_KEY, Json(&session_store));
+ }
+
+ for sub in subscribers.iter() {
+ let resp = Response::LoggedIn(true);
+ agent.link.respond(*sub, resp);
+ }
+ });
+ }
+ Request::GetLoggedIn => {
+ let login_client = self.login();
+ if login_client.is_none() {
+ for sub in self.subscribers.iter() {
+ let resp = Response::Error(MatrixError::MissingClient);
+ self.link.respond(*sub, resp);
+ }
+ return;
+ }
+
+ // Always clone agent after having tried to login!
+ let agent = self.clone();
+
+ spawn_local(async move {
+ let logged_in = agent.get_logged_in().await;
+
+ if !logged_in && agent.session.is_some() {
+ error!("Not logged in but got session");
+ } else {
+ for sub in agent.subscribers.iter() {
+ let resp = Response::LoggedIn(logged_in);
+ agent.link.respond(*sub, resp);
+ }
+ }
+ });
+ }
+ Request::StartSync => {
+ // Always clone agent after having tried to login!
+ let agent = self.clone();
+ spawn_local(async move {
+ agent.start_sync().await;
+ });
+ }
+ Request::GetJoinedRooms => {
+ let agent = self.clone();
+ spawn_local(async move {
+ let rooms: Arc<RwLock<HashMap<RoomId, Arc<RwLock<Room>>>>> =
+ agent.matrix_client.unwrap().joined_rooms();
+
+ let readable_rooms = rooms.read().await;
+ let mut rooms_unarced: HashMap<RoomId, Room> = HashMap::new();
+ for (id, room) in readable_rooms.iter() {
+ let unarced_room = (*room.write().await).clone();
+ rooms_unarced.insert(id.clone(), unarced_room);
+ }
+ for sub in agent.subscribers.iter() {
+ let resp = Response::JoinedRoomList(rooms_unarced.clone());
+ agent.link.respond(*sub, resp);
+ }
+ });
+ }
+ Request::GetUserdata => {
+ // Noop
+ }
+ Request::GetOldMessages((room_id, from)) => {
+ let agent = self.clone();
+ spawn_local(async move {
+ let mut builder = &mut MessagesRequestBuilder::new();
+ builder = builder.room_id(room_id.clone());
+ if let Some(from) = from {
+ builder = builder.from(from);
+ } else {
+ builder = builder.from(
+ agent
+ .matrix_client
+ .clone()
+ .unwrap()
+ .sync_token()
+ .await
+ .unwrap(),
+ );
+ }
+ let filter = RoomEventFilter {
+ types: Some(vec!["m.room.message".to_string()]),
+ ..Default::default()
+ };
+ builder = builder
+ .filter(filter)
+ .direction(Direction::Backward)
+ .limit(UInt::new(30).unwrap());
+
+ // TODO handle error gracefully
+ let messsages = agent
+ .matrix_client
+ .clone()
+ .unwrap()
+ .room_messages(builder.clone())
+ .await
+ .unwrap();
+ // TODO save end point for future loading
+
+ let mut wrapped_messages: Vec<MessageEvent> = Vec::new();
+ let chunk_iter: Vec<EventJson<RoomEvent>> = messsages.chunk;
+ let (oks, _): (Vec<_>, Vec<_>) = chunk_iter
+ .iter()
+ .map(|event| event.deserialize())
+ .partition(Result::is_ok);
+
+ let deserialized_events: Vec<RoomEvent> =
+ oks.into_iter().map(Result::unwrap).collect();
+
+ for event in deserialized_events.into_iter().rev() {
+ 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(),
+ image_event.url.unwrap(),
+ ));
+ image_event.url = new_url;
+ }
+ if image_event.info.is_some() {
+ 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(),
+ info.thumbnail_url.unwrap(),
+ ));
+ info.thumbnail_url = new_url;
+ }
+ image_event.info = Some(info);
+ }
+ event.content = MessageEventContent::Image(image_event);
+ }
+ 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(),
+ video_event.url.unwrap(),
+ ));
+ video_event.url = new_url;
+ }
+ if video_event.info.is_some() {
+ 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(),
+ info.thumbnail_url.unwrap(),
+ ));
+ info.thumbnail_url = new_url;
+ }
+ video_event.info = Some(info);
+ }
+ event.content = MessageEventContent::Video(video_event);
+ }
+ wrapped_messages.push(event.clone());
+ }
+ }
+
+ for sub in agent.subscribers.iter() {
+ let resp =
+ Response::OldMessages((room_id.clone(), wrapped_messages.clone()));
+ agent.link.respond(*sub, resp);
+ }
+ });
+ }
+ Request::GetJoinedRoom(room_id) => {
+ let agent = self.clone();
+ spawn_local(async move {
+ let room: Arc<RwLock<Room>> = agent
+ .matrix_client
+ .unwrap()
+ .get_joined_room(&room_id)
+ .await
+ .unwrap();
+ 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()));
+ agent.link.respond(*sub, resp);
+ }
+ });
+ }
+ Request::SendMessage((room_id, message)) => {
+ let client = self.matrix_client.clone().unwrap();
+ spawn_local(async move {
+ let mut options = Options::empty();
+ options.insert(Options::ENABLE_STRIKETHROUGH);
+ let parser = Parser::new_ext(message.as_str(), options);
+
+ let mut formatted_message: String =
+ String::with_capacity(message.len() * 3 / 2);
+ html::push_html(&mut formatted_message, parser);
+ formatted_message = formatted_message.replace("<p>", "").replace("</p>", "");
+ formatted_message.pop();
+
+ let content = if formatted_message == message {
+ MessageEventContent::Text(TextMessageEventContent::new_plain(message))
+ } else {
+ MessageEventContent::Text(TextMessageEventContent {
+ body: message,
+ relates_to: None,
+ formatted: Some(FormattedBody{
+ body: formatted_message,
+ format: MessageFormat::Html,
+ })
+ })
+ };
+ client.room_send(&room_id, content, None).await;
+ });
+ }
+ }
+ }
+
+ fn disconnected(&mut self, id: HandlerId) {
+ self.subscribers.remove(&id);
+ }
+}
+
+unsafe impl Send for MatrixAgent {}
+
+unsafe impl std::marker::Sync for MatrixAgent {}
+
+impl MatrixAgent {
+ async fn start_sync(&self) {
+ let sync = sync::Sync {
+ matrix_client: self.matrix_client.clone().unwrap(),
+ callback: self.link.callback(Msg::OnSyncResponse),
+ };
+ sync.start_sync().await;
+ }
+
+ async fn get_logged_in(&self) -> bool {
+ if self.matrix_client.is_none() {
+ return false;
+ }
+ self.matrix_client.as_ref().unwrap().logged_in().await
+ }
+
+ fn login(&mut self) -> Option<Client> {
+ if (self.matrix_state.homeserver.is_none()
+ || self.matrix_state.username.is_none()
+ || self.matrix_state.password.is_none()
+ || self.matrix_client.is_some())
+ && self.session.is_none()
+ {
+ for sub in self.subscribers.iter() {
+ let resp = Response::Error(MatrixError::MissingFields);
+ self.link.respond(*sub, resp);
+ }
+ None
+ } else if self.session.is_some() {
+ let homeserver = self.session.clone().unwrap().homeserver_url;
+
+ let client_config = ClientConfig::new();
+ let homeserver_url = Url::parse(&homeserver).unwrap();
+ let client = Client::new_with_config(homeserver_url, client_config).unwrap();
+ self.matrix_client = Some(client.clone());
+
+ // Also directly restore Login data
+ let stored_session = self.session.clone().unwrap();
+ let session = Session {
+ access_token: stored_session.access_token,
+ user_id: matrix_sdk::identifiers::UserId::try_from(stored_session.user_id.as_str())
+ .unwrap(),
+ device_id: stored_session.device_id,
+ };
+ let client_clone = client.clone();
+ spawn_local(async move {
+ client_clone.restore_login(session).await;
+ });
+
+ Some(client)
+ } else {
+ let homeserver = self.matrix_state.homeserver.clone().unwrap();
+
+ let client_config = ClientConfig::new();
+ 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)
+ }
+ }
+}
diff --git a/src/app/matrix/sync.rs b/src/app/matrix/sync.rs
@@ -8,7 +8,7 @@ use matrix_sdk::{
};
use yew::Callback;
-use crate::app::matrix::types::get_media_download_url;
+use crate::app::matrix::types::{get_media_download_url, get_video_media_download_url};
use crate::app::matrix::Response;
pub struct Sync {
@@ -45,7 +45,7 @@ impl Sync {
// TODO handle all messages...
if let RoomEvent::RoomMessage(mut event) = event {
- if let MessageEventContent::Image(mut image_event) = event.content {
+ 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(),
@@ -66,6 +66,27 @@ impl Sync {
}
event.content = MessageEventContent::Image(image_event);
}
+ 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(),
+ video_event.url.unwrap(),
+ ));
+ video_event.url = new_url;
+ }
+ if video_event.info.is_some() {
+ 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(),
+ info.thumbnail_url.unwrap(),
+ ));
+ info.thumbnail_url = new_url;
+ }
+ video_event.info = Some(info);
+ }
+ 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
@@ -13,3 +13,17 @@ pub fn get_media_download_url(client: Client, mxc_url: String) -> String {
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 {
+ 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 new_path = format!(
+ "_matrix/media/r0/download/{}/{}/fix.mp4",
+ server_name, media_id,
+ );
+ let mut new_url = client.homeserver().clone();
+ new_url.set_path(new_path.as_str());
+ new_url.to_string()
+}
diff --git a/src/app.rs b/src/app/mod.rs
diff --git a/src/app/views/main_view.rs b/src/app/views/main_view.rs
@@ -64,12 +64,25 @@ impl Component for MainView {
</div>
}
} else {
- html! {
- <div class="uk-flex auto-scrollable-container" style="height: 100%">
- <RoomList change_room_callback=self.link.callback(Msg::ChangeRoom)/>
- <EventList current_room=self.state.current_room.as_ref().unwrap() />
- </div>
+ if self.state.current_room.as_ref().unwrap().is_encrypted() {
+ html! {
+ <div class="uk-flex auto-scrollable-container" style="height: 100%">
+ <RoomList change_room_callback=self.link.callback(Msg::ChangeRoom)/>
+ <div class="container uk-flex uk-flex-column uk-width-5-6 uk-padding uk-padding-remove-bottom" style="height: 100%">
+ <h1>{ self.state.current_room.as_ref().unwrap().display_name() }</h1>
+ <h4>{ "Daydream currently does not support encryption." }</h4>
+ </div>
+ </div>
+ }
+ } else {
+ html! {
+ <div class="uk-flex auto-scrollable-container" style="height: 100%">
+ <RoomList change_room_callback=self.link.callback(Msg::ChangeRoom)/>
+ <EventList current_room=self.state.current_room.as_ref().unwrap() />
+ </div>
+ }
}
+
}
}
}