commit ebdc916cb49bcf3a7685a07abf0ac0013bb8a5a6
parent b0186dad8a6b9434b12621779b626aaad435d953
Author: Marcel <mtrnord1@gmail.com>
Date: Tue, 26 May 2020 16:26:22 +0200
Print sync and use some bootstrap css
Took 1 hour 28 minutes
Diffstat:
9 files changed, 213 insertions(+), 78 deletions(-)
diff --git a/src/app.rs b/src/app.rs
@@ -2,8 +2,8 @@ use log::*;
use yew::{prelude::*, virtual_dom::VNode};
use yew_router::{prelude::*, Switch};
+use crate::app::matrix::{MatrixAgent, Response};
use crate::app::views::{login::Login, main_view::MainView};
-use crate::app::matrix::{Response, MatrixAgent};
use yew_router::agent::RouteRequest::ChangeRoute;
mod matrix;
@@ -65,10 +65,10 @@ impl Component for App {
Msg::NewMessage(response) => {
info!("NewMessage: {:#?}", response);
match response {
- Response::Error(_) => {},
+ Response::Error(_) => {}
Response::LoggedIn(logged_in) => {
info!("client_logged_in");
- info!("{}",logged_in);
+ info!("{}", logged_in);
let route: Route = if logged_in {
//self.state.logged_in = true;
@@ -83,7 +83,8 @@ impl Component for App {
info!("{:#?}", route.clone());
self.route = Some(route.clone());
self.route_agent.send(ChangeRoute(route));
- },
+ }
+ _ => {}
}
}
}
@@ -98,28 +99,26 @@ impl Component for App {
info!("rendered App!");
info!("Route: {:#?}", &self.route);
html! {
- <div>
- {
- match &self.route {
- None => {info!("NoneRoute"); html! {<Login />}},
- Some(route) => match AppRoute::switch(route.clone()) {
- Some(AppRoute::MainView) => {
- info!("MainViewRoute");
- html! {
- <MainView />
- }
- },
- Some(AppRoute::Start) => {
- info!("StartRoute");
- html! {
- <Login />
- }
- },
- None => VNode::from("404")
- }
+ {
+ match &self.route {
+ None => {info!("NoneRoute"); html! {<Login />}},
+ Some(route) => match AppRoute::switch(route.clone()) {
+ Some(AppRoute::MainView) => {
+ info!("MainViewRoute");
+ html! {
+ <MainView />
+ }
+ },
+ Some(AppRoute::Start) => {
+ info!("StartRoute");
+ html! {
+ <Login />
+ }
+ },
+ None => VNode::from("404")
}
}
- </div>
+ }
}
}
}
diff --git a/src/app/matrix.rs b/src/app/matrix.rs
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
use std::sync::{Arc, Mutex};
use log::*;
-use matrix_sdk::{Client, ClientConfig, Session, Error};
+use matrix_sdk::{Client, ClientConfig, Session, SyncSettings};
use serde_derive::{Deserialize, Serialize};
use url::Url;
use wasm_bindgen_futures::spawn_local;
@@ -14,6 +14,8 @@ use yew::worker::*;
use crate::constants::AUTH_KEY;
use crate::errors::MatrixError;
+mod sync;
+
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub struct MatrixClient {
pub(crate) homeserver: Option<String>,
@@ -34,6 +36,7 @@ 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>,
@@ -46,12 +49,15 @@ pub enum Request {
SetPassword(String),
Login(),
GetLoggedIn,
+ StartSync,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum Response {
Error(MatrixError),
LoggedIn(bool),
+ // TODO properly handle sync events
+ Sync(String),
}
impl Agent for MatrixAgent {
@@ -61,7 +67,9 @@ impl Agent for MatrixAgent {
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 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)
@@ -112,14 +120,17 @@ impl Agent for MatrixAgent {
let password = password.clone();
let client = client.clone();
let subscribers = self.subscribers.clone();
- let mut agent = self.clone();
+ let agent = self.clone();
spawn_local(async move {
// TODO handle login error
if agent.session.is_some() {
let stored_session = agent.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(),
+ 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;
@@ -131,7 +142,8 @@ impl Agent for MatrixAgent {
None,
Some("Daydream".to_string()),
)
- .await.unwrap();
+ .await
+ .unwrap();
let session_store = SessionStore {
access_token: login_response.access_token,
user_id: login_response.user_id.to_string(),
@@ -178,6 +190,13 @@ impl Agent for MatrixAgent {
}
});
}
+ Request::StartSync => {
+ // Always clone agent after having tried to login!
+ let agent = self.clone();
+ spawn_local(async move {
+ agent.start_sync().await;
+ });
+ }
}
}
@@ -186,23 +205,35 @@ impl Agent for MatrixAgent {
}
}
+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: |x: Response| {
+ for sub in self.subscribers.iter() {
+ self.link.respond(*sub, x.clone());
+ }
+ },
+ };
+ sync.start_sync().await;
+ }
+
async fn get_logged_in(&self) -> bool {
if self.matrix_client.is_none() {
return false;
}
- self.matrix_client
- .clone()
- .unwrap()
- .logged_in()
- .await
+ self.matrix_client.clone().unwrap().logged_in().await
}
fn login(&mut self) -> Option<Client> {
return 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()
+ || self.matrix_client.is_some())
+ && self.session.is_none()
{
let resp = Response::Error(MatrixError::MissingFields);
for sub in self.subscribers.iter() {
@@ -221,7 +252,8 @@ impl MatrixAgent {
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(),
+ 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();
diff --git a/src/app/matrix/sync.rs b/src/app/matrix/sync.rs
@@ -0,0 +1,62 @@
+use crate::app::matrix::Response;
+use log::*;
+use matrix_sdk::{
+ api::r0::sync::sync_events::Response as SyncResponse,
+ events::collections::all::RoomEvent,
+ events::room::message::{MessageEvent, MessageEventContent, TextMessageEventContent},
+ identifiers::RoomId,
+ Client, SyncSettings,
+};
+
+pub struct Sync<F>
+where
+ F: Fn(Response) + std::marker::Sync,
+{
+ pub(crate) matrix_client: Client,
+ pub(crate) callback: F,
+}
+
+impl<F> Sync<F>
+where
+ F: Fn(Response) + std::marker::Sync,
+{
+ pub async fn start_sync(&self) {
+ let client = self.matrix_client.clone();
+ client.clone().sync(SyncSettings::default()).await.unwrap();
+
+ let settings = SyncSettings::default().token(client.clone().sync_token().await.unwrap());
+ client
+ .clone()
+ .sync_forever(settings, |response| self.on_sync_response(response))
+ .await;
+ }
+
+ async fn on_sync_response(&self, response: SyncResponse) {
+ info!("Synced");
+
+ for (room_id, room) in response.rooms.join {
+ for event in room.timeline.events {
+ if let Ok(event) = event.deserialize() {
+ self.on_room_message(&room_id, event).await
+ }
+ }
+ }
+ }
+
+ async fn on_room_message(&self, room_id: &RoomId, event: RoomEvent) {
+ // TODO handle all messages... (Extra class?)
+ let msg_body = if let RoomEvent::RoomMessage(MessageEvent {
+ content: MessageEventContent::Text(TextMessageEventContent { body: msg_body, .. }),
+ ..
+ }) = event
+ {
+ msg_body.clone()
+ } else {
+ return;
+ };
+
+ info!("Received message event {:?}", &msg_body);
+ let resp = Response::Sync(msg_body);
+ (self.callback)(resp);
+ }
+}
diff --git a/src/app/views/login.rs b/src/app/views/login.rs
@@ -20,7 +20,6 @@ pub enum Msg {
Nope,
}
-
impl Component for Login {
type Message = Msg;
type Properties = ();
@@ -73,35 +72,48 @@ impl Component for Login {
fn view(&self) -> Html {
info!("rendered Login!");
html! {
- <div>
- <input class="server"
- placeholder="Homeserver URL"
- value=&self.homeserver
- oninput=self.link.callback(|e: InputData| Msg::SetHomeserver(e.value))
- //onkeypress=self.link.callback(|e: KeyboardEvent| {
- // if e.key() == "Enter" { Msg::Add } else { Msg::Nope }
- //})
- />
- <input class="username"
- placeholder="MXID/Username"
- value=&self.username
- oninput=self.link.callback(|e: InputData| Msg::SetUsername(e.value))
- //onkeypress=self.link.callback(|e: KeyboardEvent| {
- // if e.key() == "Enter" { Msg::Add } else { Msg::Nope }
- //})
- />
- <input class="password"
- placeholder="Password"
- type="password"
- value=&self.password
- oninput=self.link.callback(|e: InputData| Msg::SetPassword(e.value))
- />
- <button onclick=self.link.callback(|_: MouseEvent| Msg::Login)
- onkeypress=self.link.callback(|e: KeyboardEvent| {
- if e.key() == "Enter" { Msg::Login } else { Msg::Nope }
- })>
- { "Login" }
- </button>
+ <div class="container h-100">
+ <div class="row align-items-center h-100">
+ <h1>{"Login"}</h1>
+ <form class="col-6 mx-auto" onsubmit=self.link.callback(|e: Event| {e.prevent_default(); Msg::Login})>
+ <div class="form-group">
+ <label for="homeserver">{"Homeserver URL"}</label>
+ <input
+ class="form-control"
+ type="url"
+ id="homeserver"
+ placeholder="Homeserver URL"
+ value=&self.homeserver
+ oninput=self.link.callback(|e: InputData| Msg::SetHomeserver(e.value))
+ />
+ </div>
+ <div class="form-group">
+ <label for="username">{"MXID/Username"}</label>
+ <input
+ class="form-control"
+ id="username"
+ placeholder="MXID/Username"
+ value=&self.username
+ oninput=self.link.callback(|e: InputData| Msg::SetUsername(e.value))
+ />
+ </div>
+ <div class="form-group">
+ <label for="password">{"Password"}</label>
+ <input
+ class="form-control"
+ type="password"
+ id="password"
+ placeholder="Password"
+ value=&self.password
+ oninput=self.link.callback(|e: InputData| Msg::SetPassword(e.value))
+ />
+ </div>
+
+ <button type="submit" class="btn btn-primary">
+ { "Login" }
+ </button>
+ </form>
+ </div>
</div>
}
}
diff --git a/src/app/views/main_view.rs b/src/app/views/main_view.rs
@@ -3,7 +3,8 @@ use serde_derive::{Deserialize, Serialize};
use yew::prelude::*;
use yew::ComponentLink;
-use crate::app::matrix::{MatrixAgent, Response};
+use crate::app::matrix::{MatrixAgent, Request, Response};
+use wasm_bindgen::__rt::std::collections::{HashMap, HashSet};
pub struct MainView {
link: ComponentLink<Self>,
@@ -15,8 +16,11 @@ pub enum Msg {
NewMessage(Response),
}
-#[derive(Serialize, Deserialize)]
-pub struct State {}
+#[derive(Serialize, Deserialize, Default)]
+pub struct State {
+ // TODO handle all events
+ pub events: HashSet<String>,
+}
impl Component for MainView {
type Message = Msg;
@@ -24,8 +28,11 @@ impl Component for MainView {
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
let matrix_callback = link.callback(Msg::NewMessage);
- let matrix_agent = MatrixAgent::bridge(matrix_callback);
- let state = State {};
+ let mut matrix_agent = MatrixAgent::bridge(matrix_callback);
+ matrix_agent.send(Request::StartSync);
+ let state = State {
+ events: Default::default(),
+ };
MainView {
link,
@@ -41,7 +48,11 @@ impl Component for MainView {
match response {
Response::LoggedIn(v) => {
info!("client_logged_in: {}", v);
- },
+ }
+ Response::Sync(msg) => {
+ // TODO handle all events
+ self.state.events.insert(msg);
+ }
_ => {}
}
}
@@ -56,7 +67,17 @@ impl Component for MainView {
fn view(&self) -> Html {
info!("rendered MainView!");
html! {
- <p>{"Test"}</p>
+ <div class="container h-100">
+ { self.state.events.iter().map(|event| self.get_event(event)).collect::<Html>() }
+ </div>
+ }
+ }
+}
+
+impl MainView {
+ fn get_event(&self, event: &String) -> Html {
+ html! {
+ <><p>{event}</p><br/></>
}
}
}
diff --git a/src/errors.rs b/src/errors.rs
@@ -1,5 +1,5 @@
-use serde_derive::{Deserialize, Serialize};use thiserror::Error;
-
+use serde_derive::{Deserialize, Serialize};
+use thiserror::Error;
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum MatrixError {
diff --git a/src/lib.rs b/src/lib.rs
@@ -1,11 +1,10 @@
-
#![recursion_limit = "512"]
#[macro_use]
extern crate cfg_if;
mod app;
-mod errors;
mod constants;
+mod errors;
use wasm_bindgen::prelude::*;
@@ -31,7 +30,6 @@ cfg_if! {
}
}
-
// This is the entry point for the web app
#[wasm_bindgen]
pub fn run_app() -> Result<(), JsValue> {
diff --git a/static/index.html b/static/index.html
@@ -2,9 +2,18 @@
<html lang="en">
<head>
<meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Daydream</title>
+ <!-- TODO Serve locally!-->
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
</head>
<body>
<script src="/daydream.js"></script>
+
+ <!-- TODO Serve locally!-->
+ <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
+ <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
+ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
</body>
</html>
diff --git a/static/style.scss b/static/style.scss
@@ -0,0 +1,2 @@
+html { height: 100%; }
+body { height: 100%; }