⬆️ Update the components to take the dioxus 0.5 rework into account
This commit is contained in:
13
Cargo.toml
13
Cargo.toml
@@ -6,11 +6,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dioxus = "0.4.3"
|
dioxus = "0.5.0"
|
||||||
dioxus-desktop = "0.4.3"
|
dioxus-desktop = "0.5.0"
|
||||||
dioxus-free-icons = { version = "0.7.0", features = ["material-design-icons-navigation", "ionicons"] }
|
dioxus-free-icons = { version = "0.8", features = ["material-design-icons-navigation", "ionicons"] }
|
||||||
dioxus-std = { version = "0.4.1", features = ["utils"] }
|
dioxus-std = { git = "https://github.com/DioxusLabs/dioxus-std.git", branch = "master", features = ["utils"] }
|
||||||
fermi = { version = "0.4.3" }
|
|
||||||
|
|
||||||
# matrix-sdk = { version = "0.6.2", features = ["js"] }
|
# matrix-sdk = { version = "0.6.2", features = ["js"] }
|
||||||
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", branch = "main" , features = ["js"]}
|
matrix-sdk = { git = "https://github.com/matrix-org/matrix-rust-sdk.git", branch = "main" , features = ["js"]}
|
||||||
@@ -41,3 +40,7 @@ regex = "1.10.3"
|
|||||||
|
|
||||||
[package.metadata.turf.class_names]
|
[package.metadata.turf.class_names]
|
||||||
template = "<original_name>--<id>"
|
template = "<original_name>--<id>"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
desktop = ["dioxus/desktop"]
|
||||||
|
@@ -38,5 +38,6 @@ learn Rust) graphical library should be selected. The [Dioxus](https://dioxuslab
|
|||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
|
- [ ] Test dioxus-radio.
|
||||||
- [ ] Design system ?
|
- [ ] Design system ?
|
||||||
- [ ] Implement MSN messenger features using Matrix.org sdk...
|
- [ ] Implement MSN messenger features using Matrix.org sdk...
|
||||||
|
61
src/base.rs
61
src/base.rs
@@ -3,10 +3,13 @@
|
|||||||
// (used by [UnboundedReceiver]) by adding 'futures_util' as a dependency to your project
|
// (used by [UnboundedReceiver]) by adding 'futures_util' as a dependency to your project
|
||||||
// and adding the use futures_util::stream::StreamExt;
|
// and adding the use futures_util::stream::StreamExt;
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
|
use crate::matrix_interface::client::{Client, RoomEvent};
|
||||||
|
use crate::matrix_interface::requester::{Receivers, Requester};
|
||||||
|
use crate::matrix_interface::worker_tasks::LoginStyle;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use fermi::*;
|
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
room::{Room as MatrixRoom, RoomMember},
|
room::{Room as MatrixRoom, RoomMember},
|
||||||
ruma::{OwnedRoomId, OwnedUserId},
|
ruma::{OwnedRoomId, OwnedUserId},
|
||||||
@@ -15,9 +18,6 @@ use tokio::select;
|
|||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
|
|
||||||
use crate::components::chats_window::interface::Interface as ChatsWinInterface;
|
use crate::components::chats_window::interface::Interface as ChatsWinInterface;
|
||||||
use crate::matrix_interface::client::{Client, RoomEvent};
|
|
||||||
use crate::matrix_interface::requester::{Receivers, Requester};
|
|
||||||
use crate::matrix_interface::worker_tasks::LoginStyle;
|
|
||||||
|
|
||||||
// #[derive(Clone, Debug)]
|
// #[derive(Clone, Debug)]
|
||||||
// pub struct UserInfo {
|
// pub struct UserInfo {
|
||||||
@@ -140,11 +140,9 @@ impl AppSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static APP_SETTINGS: AtomRef<AppSettings> = AtomRef(|_| AppSettings::new());
|
async fn on_room(room_id: OwnedRoomId, room: Room, by_id_rooms: &GlobalSignal<ByIdRooms>) {
|
||||||
|
|
||||||
async fn on_room(room_id: OwnedRoomId, room: Room, rooms_ref: &UseAtomRef<ByIdRooms>) {
|
|
||||||
// TODO: Update rooms
|
// TODO: Update rooms
|
||||||
rooms_ref
|
by_id_rooms
|
||||||
.write()
|
.write()
|
||||||
.insert(room_id, RefCell::<Room>::new(room));
|
.insert(room_id, RefCell::<Room>::new(room));
|
||||||
}
|
}
|
||||||
@@ -152,31 +150,36 @@ async fn on_room(room_id: OwnedRoomId, room: Room, rooms_ref: &UseAtomRef<ByIdRo
|
|||||||
async fn on_joining_invitation(
|
async fn on_joining_invitation(
|
||||||
room_id: OwnedRoomId,
|
room_id: OwnedRoomId,
|
||||||
room: Room,
|
room: Room,
|
||||||
rooms_ref: &UseAtomRef<ByIdRooms>,
|
by_id_rooms: &GlobalSignal<ByIdRooms>,
|
||||||
) {
|
) {
|
||||||
debug!(
|
debug!(
|
||||||
"You're invited to join the \"{}\" room",
|
"You're invited to join the \"{}\" room",
|
||||||
room.name().unwrap()
|
room.name().unwrap()
|
||||||
);
|
);
|
||||||
// TODO: Update rooms
|
// TODO: Update rooms
|
||||||
rooms_ref
|
by_id_rooms
|
||||||
.write()
|
.write()
|
||||||
.insert(room_id, RefCell::<Room>::new(room));
|
.insert(room_id, RefCell::<Room>::new(room));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_room_topic(room_id: OwnedRoomId, topic: String, rooms_ref: &UseAtomRef<ByIdRooms>) {
|
async fn on_room_topic(room_id: OwnedRoomId, topic: String, by_id_rooms: &GlobalSignal<ByIdRooms>) {
|
||||||
if let Some(room_ref) = rooms_ref.read().get(&room_id) {
|
if let Some(room) = by_id_rooms.read().get(&room_id) {
|
||||||
let mut room = room_ref.borrow_mut();
|
let mut room = room.borrow_mut();
|
||||||
room.topic = Some(RefCell::new(topic));
|
room.topic = Some(RefCell::new(topic));
|
||||||
} else {
|
} else {
|
||||||
warn!("No room found with the \"{}\" id", room_id);
|
warn!("No room found with the \"{}\" id", room_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn sync_messages(by_id_rooms: &GlobalSignal<ByIdRooms>, room_id: OwnedRoomId) {
|
||||||
|
error!("== sync_messages ==");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn sync_rooms(
|
pub async fn sync_rooms(
|
||||||
mut rx: UnboundedReceiver<bool>,
|
mut rx: UnboundedReceiver<bool>,
|
||||||
receivers: Receivers,
|
receivers: Receivers,
|
||||||
rooms_ref: UseAtomRef<ByIdRooms>,
|
by_id_rooms: &GlobalSignal<ByIdRooms>,
|
||||||
) {
|
) {
|
||||||
if let Some(_is_logged) = rx.next().await {
|
if let Some(_is_logged) = rx.next().await {
|
||||||
let mut rooms_receiver = receivers.room_receiver.borrow_mut();
|
let mut rooms_receiver = receivers.room_receiver.borrow_mut();
|
||||||
@@ -187,9 +190,9 @@ pub async fn sync_rooms(
|
|||||||
res = rooms_receiver.recv() => {
|
res = rooms_receiver.recv() => {
|
||||||
if let Ok(room_event) = res {
|
if let Ok(room_event) = res {
|
||||||
match room_event {
|
match room_event {
|
||||||
RoomEvent::MemberEvent(room_id, room) => on_room(room_id, room, &rooms_ref).await,
|
RoomEvent::MemberEvent(room_id, room) => on_room(room_id, room, &by_id_rooms).await,
|
||||||
RoomEvent::InviteEvent(room_id, room) => on_joining_invitation(room_id, room, &rooms_ref).await,
|
RoomEvent::InviteEvent(room_id, room) => on_joining_invitation(room_id, room, &by_id_rooms).await,
|
||||||
RoomEvent::TopicEvent(room_id, topic) => on_room_topic(room_id, topic, &rooms_ref).await,
|
RoomEvent::TopicEvent(room_id, topic) => on_room_topic(room_id, topic, &by_id_rooms).await,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -200,14 +203,14 @@ pub async fn sync_rooms(
|
|||||||
|
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
mut rx: UnboundedReceiver<bool>,
|
mut rx: UnboundedReceiver<bool>,
|
||||||
app_settings_ref: UseAtomRef<AppSettings>,
|
app_settings: &GlobalSignal<AppSettings>,
|
||||||
session_ref: UseAtomRef<Session>,
|
session: &GlobalSignal<Session>,
|
||||||
) {
|
) {
|
||||||
while let Some(is_logged) = rx.next().await {
|
while let Some(is_logged) = rx.next().await {
|
||||||
if !is_logged {
|
if !is_logged {
|
||||||
let homeserver_url = session_ref.read().homeserver_url.clone();
|
let homeserver_url = session.read().homeserver_url.clone();
|
||||||
let username = session_ref.read().username.clone();
|
let username = session.read().username.clone();
|
||||||
let password = session_ref.read().password.clone();
|
let password = session.read().password.clone();
|
||||||
|
|
||||||
if homeserver_url.is_some() && username.is_some() && password.is_some() {
|
if homeserver_url.is_some() && username.is_some() && password.is_some() {
|
||||||
let client = Client::spawn(homeserver_url.unwrap()).await;
|
let client = Client::spawn(homeserver_url.unwrap()).await;
|
||||||
@@ -222,14 +225,14 @@ pub async fn login(
|
|||||||
{
|
{
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
debug!("successfully logged");
|
debug!("successfully logged");
|
||||||
session_ref.write().is_logged = true;
|
session.write().is_logged = true;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Error during login: {err}");
|
error!("Error during login: {err}");
|
||||||
// invalid_login.modify(|_| true);
|
// invalid_login.modify(|_| true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app_settings_ref.write().set_requester(RefCell::new(client));
|
app_settings.write().set_requester(RefCell::new(client));
|
||||||
} else {
|
} else {
|
||||||
warn!("At least one of the following values is/are invalid: homeserver, username or password");
|
warn!("At least one of the following values is/are invalid: homeserver, username or password");
|
||||||
}
|
}
|
||||||
@@ -247,7 +250,7 @@ pub struct Session {
|
|||||||
pub is_logged: bool,
|
pub is_logged: bool,
|
||||||
}
|
}
|
||||||
impl Session {
|
impl Session {
|
||||||
fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
homeserver_url: None,
|
homeserver_url: None,
|
||||||
username: None,
|
username: None,
|
||||||
@@ -267,6 +270,8 @@ impl Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static ROOMS: AtomRef<ByIdRooms> = AtomRef(|_| ByIdRooms::new());
|
pub static APP_SETTINGS: GlobalSignal<AppSettings> = Signal::global(AppSettings::new);
|
||||||
pub static SESSION: AtomRef<Session> = AtomRef(|_| Session::new());
|
pub static ROOMS: GlobalSignal<ByIdRooms> = Signal::global(ByIdRooms::new);
|
||||||
pub static CHATS_WIN_INTERFACE: AtomRef<ChatsWinInterface> = AtomRef(|_| ChatsWinInterface::new());
|
pub static SESSION: GlobalSignal<Session> = Signal::global(Session::new);
|
||||||
|
pub static CHATS_WIN_INTERFACE: GlobalSignal<ChatsWinInterface> =
|
||||||
|
Signal::global(ChatsWinInterface::new);
|
||||||
|
@@ -2,9 +2,9 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/avatar_selector.scss");
|
turf::style_sheet!("src/components/avatar_selector.scss");
|
||||||
|
|
||||||
pub fn AvatarSelector(cx: Scope) -> Element {
|
pub fn AvatarSelector() -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::AVATAR_SELECTOR,
|
class: ClassName::AVATAR_SELECTOR,
|
||||||
@@ -50,5 +50,5 @@ pub fn AvatarSelector(cx: Scope) -> Element {
|
|||||||
src: "./images/default-avatar.png",
|
src: "./images/default-avatar.png",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,19 @@ use dioxus_free_icons::{Icon, IconShape};
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/button.scss");
|
turf::style_sheet!("src/components/button.scss");
|
||||||
|
|
||||||
|
#[derive(PartialEq, Clone, Props)]
|
||||||
|
struct _ButtonProps {
|
||||||
|
children: Element,
|
||||||
|
#[props(default = false)]
|
||||||
|
focus: bool,
|
||||||
|
id: Option<String>,
|
||||||
|
onclick: Option<EventHandler<MouseEvent>>,
|
||||||
|
style: String,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! svg_text_icon {
|
macro_rules! svg_text_icon {
|
||||||
($name:ident,$text:literal) => {
|
($name:ident,$text:literal) => {
|
||||||
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
struct $name;
|
struct $name;
|
||||||
impl IconShape for $name {
|
impl IconShape for $name {
|
||||||
fn view_box(&self) -> String {
|
fn view_box(&self) -> String {
|
||||||
@@ -15,7 +26,7 @@ macro_rules! svg_text_icon {
|
|||||||
String::from("http://www.w3.org/2000/svg")
|
String::from("http://www.w3.org/2000/svg")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_elements(&self) -> LazyNodes {
|
fn child_elements(&self) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
text {
|
text {
|
||||||
x: "50%",
|
x: "50%",
|
||||||
@@ -30,78 +41,64 @@ macro_rules! svg_text_icon {
|
|||||||
|
|
||||||
macro_rules! svg_text_button {
|
macro_rules! svg_text_button {
|
||||||
($name:ident,$style:ident,$icon:ident) => {
|
($name:ident,$style:ident,$icon:ident) => {
|
||||||
pub fn $name<'a>(cx: Scope<'a, ButtonProps>) -> Element<'a> {
|
pub fn $name(props: ButtonProps) -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: cx.props.id.unwrap_or(""),
|
id: props.id,
|
||||||
|
|
||||||
style: ClassName::$style,
|
style: {ClassName::$style},
|
||||||
|
|
||||||
onclick: |event| {
|
onclick: move |event| {
|
||||||
if let Some(cb) = &cx.props.onclick {
|
if let Some(cb) = &props.onclick {
|
||||||
cb.call(event);
|
cb.call(event);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
focus: cx.props.focus,
|
focus: props.focus,
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
icon: $icon,
|
icon: $icon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(PartialEq, Clone, Props)]
|
||||||
struct _ButtonProps<'a> {
|
pub struct ButtonProps {
|
||||||
children: Element<'a>,
|
|
||||||
#[props(default = false)]
|
#[props(default = false)]
|
||||||
focus: bool,
|
focus: bool,
|
||||||
#[props(optional)]
|
id: Option<String>,
|
||||||
id: Option<&'a str>,
|
onclick: Option<EventHandler<MouseEvent>>,
|
||||||
#[props(optional)]
|
style: Option<String>,
|
||||||
onclick: Option<EventHandler<'a, MouseEvent>>,
|
children: Element,
|
||||||
style: &'static str,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Button<'a>(cx: Scope<'a, _ButtonProps<'a>>) -> Element<'a> {
|
fn Button(props: ButtonProps) -> Element {
|
||||||
let focus = cx.props.focus;
|
rsx! {
|
||||||
|
style { {STYLE_SHEET} },
|
||||||
cx.render(rsx! {
|
|
||||||
style { STYLE_SHEET },
|
|
||||||
|
|
||||||
button {
|
button {
|
||||||
id: cx.props.id,
|
id: props.id,
|
||||||
|
|
||||||
class: cx.props.style,
|
class: props.style,
|
||||||
|
|
||||||
onmounted: move |cx| async move {
|
onmounted: move |evt| async move {
|
||||||
let _ = cx.inner().set_focus(focus).await;
|
_ = evt.set_focus(props.focus).await;
|
||||||
},
|
},
|
||||||
|
|
||||||
onclick: move |evt| {
|
onclick: move |evt| {
|
||||||
if let Some(cb) = &cx.props.onclick {
|
if let Some(cb) = &props.onclick {
|
||||||
cb.call(evt);
|
cb.call(evt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
&cx.props.children
|
{props.children},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Props)]
|
|
||||||
pub struct ButtonProps<'a> {
|
|
||||||
#[props(default = false)]
|
|
||||||
focus: bool,
|
|
||||||
#[props(optional)]
|
|
||||||
id: Option<&'a str>,
|
|
||||||
#[props(optional)]
|
|
||||||
onclick: Option<EventHandler<'a, MouseEvent>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
svg_text_icon!(RegisterText, "REGISTER");
|
svg_text_icon!(RegisterText, "REGISTER");
|
||||||
|
@@ -1,17 +1,24 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use tracing::debug;
|
use matrix_sdk::ruma::OwnedRoomId;
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use super::edit_section::EditSection;
|
use super::edit_section::EditSection;
|
||||||
|
use crate::base::{sync_messages, ROOMS};
|
||||||
use crate::components::avatar_selector::AvatarSelector;
|
use crate::components::avatar_selector::AvatarSelector;
|
||||||
use crate::components::icons::DownArrowIcon;
|
use crate::components::icons::DownArrowIcon;
|
||||||
|
|
||||||
turf::style_sheet!("src/components/chats_window/conversation.scss");
|
turf::style_sheet!("src/components/chats_window/conversation.scss");
|
||||||
|
|
||||||
pub(super) fn Conversation(cx: Scope) -> Element {
|
#[component]
|
||||||
debug!("Conversation {} rendering", "TBD");
|
pub(super) fn Conversation(room_id: OwnedRoomId) -> Element {
|
||||||
|
error!("Conversation {} rendering", room_id);
|
||||||
|
|
||||||
cx.render(rsx! {
|
let _sync_message_coro: Coroutine<()> =
|
||||||
style { STYLE_SHEET },
|
use_coroutine(|_: UnboundedReceiver<_>| sync_messages(&ROOMS, room_id));
|
||||||
|
|
||||||
|
|
||||||
|
rsx! {
|
||||||
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::CONVERSATION,
|
class: ClassName::CONVERSATION,
|
||||||
@@ -76,5 +83,5 @@ pub(super) fn Conversation(cx: Scope) -> Element {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,9 +2,9 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/chats_window/edit_section.scss");
|
turf::style_sheet!("src/components/chats_window/edit_section.scss");
|
||||||
|
|
||||||
pub fn EditSection(cx: Scope) -> Element {
|
pub fn EditSection() -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::INPUT_AREA,
|
class: ClassName::INPUT_AREA,
|
||||||
@@ -48,5 +48,5 @@ pub fn EditSection(cx: Scope) -> Element {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
use dioxus::prelude::*;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use matrix_sdk::ruma::OwnedRoomId;
|
use matrix_sdk::ruma::OwnedRoomId;
|
||||||
use tokio::sync::broadcast::error::SendError;
|
use tokio::sync::broadcast::error::SendError;
|
||||||
use tokio::sync::broadcast::{channel, Receiver, Sender};
|
use tokio::sync::broadcast::{channel, Receiver, Sender};
|
||||||
|
@@ -3,10 +3,10 @@ mod edit_section;
|
|||||||
pub mod interface;
|
pub mod interface;
|
||||||
mod navbar;
|
mod navbar;
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use fermi::*;
|
|
||||||
use matrix_sdk::ruma::OwnedRoomId;
|
use matrix_sdk::ruma::OwnedRoomId;
|
||||||
use tokio::sync::broadcast::Receiver;
|
use tokio::sync::broadcast::Receiver;
|
||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
@@ -20,32 +20,31 @@ use interface::{Interface, Tasks};
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/chats_window/chats_window.scss");
|
turf::style_sheet!("src/components/chats_window/chats_window.scss");
|
||||||
|
|
||||||
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct ChatsWindowProps {
|
pub struct ChatsWindowProps {
|
||||||
pub receivers: Receivers,
|
pub receivers: Receivers,
|
||||||
pub interface: UseAtomRef<Interface>,
|
pub interface: Signal<Interface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_rooms_tabs<'a>(
|
fn render_rooms_tabs(
|
||||||
rooms_atom_ref: &'a UseAtomRef<HashMap<OwnedRoomId, RefCell<Room>>>,
|
by_id_rooms: &GlobalSignal<HashMap<OwnedRoomId, RefCell<Room>>>,
|
||||||
displayed_room_ids_ref: &'a UseRef<HashSet<OwnedRoomId>>,
|
displayed_room_ids: Signal<HashSet<OwnedRoomId>>,
|
||||||
) -> Vec<LazyNodes<'a, 'a>> {
|
) -> Vec<Element> {
|
||||||
let rooms_ref = rooms_atom_ref.read();
|
let rooms_ref = by_id_rooms.read();
|
||||||
let displayed_room_ids = displayed_room_ids_ref.read();
|
let displayed_room_ids = displayed_room_ids.read();
|
||||||
rooms_ref
|
rooms_ref
|
||||||
.values()
|
.values()
|
||||||
.filter(|room| displayed_room_ids.contains(&room.borrow().id()))
|
.filter(|room| displayed_room_ids.contains(&room.borrow().id()))
|
||||||
.map(|room| -> LazyNodes {
|
.map(|room| {
|
||||||
let room = room.borrow();
|
let room = room.borrow();
|
||||||
let room_name = room.name().unwrap_or(room.id().to_string());
|
let room_name = room.name().unwrap_or(room.id().to_string());
|
||||||
rsx!(
|
rsx!(
|
||||||
div {
|
div {
|
||||||
class: ClassName::TAB,
|
class: ClassName::TAB,
|
||||||
|
|
||||||
button {
|
button {
|
||||||
img {
|
img {
|
||||||
src: "./images/status_online.png",
|
src: "./images/status_online.png",
|
||||||
},
|
},
|
||||||
|
|
||||||
"{room_name}",
|
"{room_name}",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -54,9 +53,25 @@ fn render_rooms_tabs<'a>(
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_controls<'a>(
|
fn render_rooms_conversations(
|
||||||
receiver_ref: &'a RefCell<Receiver<Tasks>>,
|
by_id_rooms: &GlobalSignal<HashMap<OwnedRoomId, RefCell<Room>>>,
|
||||||
displayed_room_ids_ref: &'a UseRef<HashSet<OwnedRoomId>>,
|
displayed_room_ids: Signal<HashSet<OwnedRoomId>>,
|
||||||
|
) -> Vec<Element> {
|
||||||
|
let rooms_ref = by_id_rooms.read();
|
||||||
|
let displayed_room_ids = displayed_room_ids.read();
|
||||||
|
rooms_ref
|
||||||
|
.values()
|
||||||
|
.filter(|room| displayed_room_ids.contains(&room.borrow().id()))
|
||||||
|
.map(|room| {
|
||||||
|
let room_id = room.borrow().id();
|
||||||
|
rsx!(Conversation { room_id: room_id },)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_controls(
|
||||||
|
receiver_ref: &RefCell<Receiver<Tasks>>,
|
||||||
|
mut displayed_room_ids: Signal<HashSet<OwnedRoomId>>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
let result = receiver_ref.borrow_mut().recv().await;
|
let result = receiver_ref.borrow_mut().recv().await;
|
||||||
@@ -64,7 +79,7 @@ async fn handle_controls<'a>(
|
|||||||
Ok(task) => match task {
|
Ok(task) => match task {
|
||||||
Tasks::ToggleRoom(room_id) => {
|
Tasks::ToggleRoom(room_id) => {
|
||||||
error!("ON TOGGLE ROOM {}", room_id);
|
error!("ON TOGGLE ROOM {}", room_id);
|
||||||
let mut displayed_room_ids = displayed_room_ids_ref.write();
|
let mut displayed_room_ids = displayed_room_ids.write();
|
||||||
match displayed_room_ids.take(&room_id) {
|
match displayed_room_ids.take(&room_id) {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
error!("{} room already dispayed... close it", room_id);
|
error!("{} room already dispayed... close it", room_id);
|
||||||
@@ -81,45 +96,41 @@ async fn handle_controls<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ChatsWindow(cx: Scope<ChatsWindowProps>) -> Element {
|
pub fn ChatsWindow(props: ChatsWindowProps) -> Element {
|
||||||
debug!("ChatsWindow rendering");
|
debug!("ChatsWindow rendering");
|
||||||
|
|
||||||
use_init_atom_root(cx);
|
let receivers = &props.receivers;
|
||||||
|
let interface_ref = &props.interface;
|
||||||
|
|
||||||
let receivers = &cx.props.receivers;
|
let displayed_room_ids = use_signal(HashSet::<OwnedRoomId>::new);
|
||||||
let interface_ref = &cx.props.interface;
|
|
||||||
|
|
||||||
let rooms_ref = use_atom_ref(cx, &ROOMS);
|
let sync_rooms_coro = use_coroutine(|rx| {
|
||||||
|
|
||||||
let displayed_room_ids = use_ref(cx, HashSet::<OwnedRoomId>::new);
|
|
||||||
|
|
||||||
let sync_rooms_coro = use_coroutine(cx, |rx| {
|
|
||||||
to_owned![receivers];
|
to_owned![receivers];
|
||||||
sync_rooms(rx, receivers, rooms_ref.clone())
|
sync_rooms(rx, receivers, &ROOMS)
|
||||||
});
|
});
|
||||||
sync_rooms_coro.send(true);
|
sync_rooms_coro.send(true);
|
||||||
|
|
||||||
let _: &Coroutine<()> = use_coroutine(cx, |_: UnboundedReceiver<_>| {
|
let _: Coroutine<()> = use_coroutine(|_: UnboundedReceiver<_>| {
|
||||||
to_owned![interface_ref, displayed_room_ids];
|
to_owned![interface_ref, displayed_room_ids];
|
||||||
async move {
|
async move {
|
||||||
let interface = interface_ref.read();
|
let interface = interface_ref.read();
|
||||||
let receiver = &interface.receiver();
|
let receiver = &interface.receiver();
|
||||||
handle_controls(receiver, &displayed_room_ids).await
|
handle_controls(receiver, displayed_room_ids).await
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let rendered_rooms_tabs = render_rooms_tabs(rooms_ref, displayed_room_ids);
|
let rendered_rooms_tabs = render_rooms_tabs(&ROOMS, displayed_room_ids);
|
||||||
|
let rendered_rooms_conversations = render_rooms_conversations(&ROOMS, displayed_room_ids);
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::CHATS_WINDOW,
|
class: ClassName::CHATS_WINDOW,
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::TABS,
|
class: ClassName::TABS,
|
||||||
|
{rendered_rooms_tabs.into_iter()},
|
||||||
rendered_rooms_tabs.into_iter(),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
@@ -133,13 +144,11 @@ pub fn ChatsWindow(cx: Scope<ChatsWindowProps>) -> Element {
|
|||||||
|
|
||||||
p {
|
p {
|
||||||
class: ClassName::ROOM_NAME,
|
class: ClassName::ROOM_NAME,
|
||||||
|
|
||||||
"MON POTE",
|
"MON POTE",
|
||||||
},
|
},
|
||||||
|
|
||||||
p {
|
p {
|
||||||
class: ClassName::ROOM_TOPIC,
|
class: ClassName::ROOM_TOPIC,
|
||||||
|
|
||||||
"LE STATUT A MON POTE",
|
"LE STATUT A MON POTE",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -147,8 +156,8 @@ pub fn ChatsWindow(cx: Scope<ChatsWindowProps>) -> Element {
|
|||||||
Navbar {},
|
Navbar {},
|
||||||
},
|
},
|
||||||
|
|
||||||
Conversation {},
|
{rendered_rooms_conversations.into_iter()},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,11 @@ use tracing::debug;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/chats_window/navbar.scss");
|
turf::style_sheet!("src/components/chats_window/navbar.scss");
|
||||||
|
|
||||||
pub fn Navbar(cx: Scope) -> Element {
|
pub fn Navbar() -> Element {
|
||||||
debug!("Navbar rendering");
|
debug!("Navbar rendering");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::NAVBAR,
|
class: ClassName::NAVBAR,
|
||||||
@@ -46,5 +46,5 @@ pub fn Navbar(cx: Scope) -> Element {
|
|||||||
style: "background: url(./images/settings.png) center no-repeat",
|
style: "background: url(./images/settings.png) center no-repeat",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
@@ -7,19 +9,18 @@ use crate::components::contacts_window::contacts_section::{
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/contacts_window/contacts.scss");
|
turf::style_sheet!("src/components/contacts_window/contacts.scss");
|
||||||
|
|
||||||
pub fn Contacts(cx: Scope) -> Element {
|
pub fn Contacts() -> Element {
|
||||||
debug!("Contacts rendering");
|
debug!("Contacts rendering");
|
||||||
|
|
||||||
// TODO: Test overflow
|
// TODO: Test overflow
|
||||||
// TODO: Add offline users ?
|
// TODO: Add offline users ?
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::CONTACTS,
|
class: ClassName::CONTACTS,
|
||||||
|
ContactsSection {name: "Groups", filter: Rc::new(filter_room_conversations)},
|
||||||
ContactsSection {name: "Groups", filter: &filter_room_conversations},
|
ContactsSection {name: "Available", filter: Rc::new(filter_people_conversations)},
|
||||||
ContactsSection {name: "Available", filter: &filter_people_conversations},
|
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,37 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_free_icons::icons::io_icons::IoChevronDown;
|
use dioxus_free_icons::icons::io_icons::IoChevronDown;
|
||||||
use dioxus_free_icons::Icon;
|
use dioxus_free_icons::Icon;
|
||||||
use fermi::prelude::*;
|
|
||||||
use matrix_sdk::{ruma::OwnedRoomId, RoomState};
|
use matrix_sdk::{ruma::OwnedRoomId, RoomState};
|
||||||
use tracing::{debug, warn};
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::base::{ByIdRooms, Room, CHATS_WIN_INTERFACE, ROOMS};
|
use crate::base::{ByIdRooms, Room, CHATS_WIN_INTERFACE, ROOMS};
|
||||||
use crate::components::chats_window::interface::Interface as ChatsWindowInterface;
|
use crate::components::chats_window::interface::Interface as ChatsWindowInterface;
|
||||||
|
|
||||||
turf::style_sheet!("src/components/contacts_window/contacts_section.scss");
|
turf::style_sheet!("src/components/contacts_window/contacts_section.scss");
|
||||||
|
|
||||||
fn ContactsArrow(cx: Scope) -> Element {
|
fn ContactsArrow() -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
icon: IoChevronDown,
|
icon: IoChevronDown,
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static NO_NAME_REPR: &str = "No name";
|
static NO_NAME_REPR: &str = "No name";
|
||||||
static NO_SUBJECT_REPR: &str = "No subject";
|
static NO_SUBJECT_REPR: &str = "No subject";
|
||||||
|
|
||||||
pub(super) fn filter_people_conversations(rooms_atom: UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>> {
|
pub(super) fn filter_people_conversations(
|
||||||
let rooms = rooms_atom.read();
|
by_id_rooms: &GlobalSignal<ByIdRooms>,
|
||||||
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(rooms.len());
|
) -> Vec<RefCell<Room>> {
|
||||||
|
let by_id_rooms = by_id_rooms.read();
|
||||||
|
|
||||||
for room in rooms.values() {
|
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(by_id_rooms.len());
|
||||||
|
|
||||||
|
for room in by_id_rooms.values() {
|
||||||
let is_direct = room.borrow().is_direct.unwrap();
|
let is_direct = room.borrow().is_direct.unwrap();
|
||||||
if !is_direct {
|
if !is_direct {
|
||||||
filtered_rooms.push(room.to_owned());
|
filtered_rooms.push(room.to_owned());
|
||||||
@@ -36,11 +40,14 @@ pub(super) fn filter_people_conversations(rooms_atom: UseAtomRef<ByIdRooms>) ->
|
|||||||
filtered_rooms
|
filtered_rooms
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn filter_room_conversations(rooms_atom: UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>> {
|
pub(super) fn filter_room_conversations(
|
||||||
let rooms = rooms_atom.read();
|
by_id_rooms: &GlobalSignal<ByIdRooms>,
|
||||||
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(rooms.len());
|
) -> Vec<RefCell<Room>> {
|
||||||
|
let by_id_rooms = by_id_rooms.read();
|
||||||
|
|
||||||
for room in rooms.values() {
|
let mut filtered_rooms = Vec::<RefCell<Room>>::with_capacity(by_id_rooms.len());
|
||||||
|
|
||||||
|
for room in by_id_rooms.values() {
|
||||||
let is_direct = room.borrow().is_direct.unwrap();
|
let is_direct = room.borrow().is_direct.unwrap();
|
||||||
if is_direct {
|
if is_direct {
|
||||||
filtered_rooms.push(room.to_owned());
|
filtered_rooms.push(room.to_owned());
|
||||||
@@ -52,30 +59,33 @@ pub(super) fn filter_room_conversations(rooms_atom: UseAtomRef<ByIdRooms>) -> Ve
|
|||||||
// TODO: Handle errors
|
// TODO: Handle errors
|
||||||
fn on_clicked_room(
|
fn on_clicked_room(
|
||||||
room_id: &OwnedRoomId,
|
room_id: &OwnedRoomId,
|
||||||
chats_window_interface: &UseAtomRef<ChatsWindowInterface>,
|
chats_window_interface: &GlobalSignal<ChatsWindowInterface>,
|
||||||
) {
|
) {
|
||||||
let _ = chats_window_interface.read().toggle_room(room_id.clone());
|
let _ = chats_window_interface.read().toggle_room(room_id.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[derive(Props, Clone)]
|
||||||
pub fn ContactsSection<'a>(
|
pub struct ContactsSectionProps {
|
||||||
cx: Scope,
|
name: String,
|
||||||
name: &'a str,
|
filter: Rc<dyn Fn(&GlobalSignal<ByIdRooms>) -> Vec<RefCell<Room>>>,
|
||||||
filter: &'a dyn Fn(UseAtomRef<ByIdRooms>) -> Vec<RefCell<Room>>,
|
}
|
||||||
) -> Element {
|
impl PartialEq for ContactsSectionProps {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.name == other.name && Rc::ptr_eq(&self.filter, &other.filter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ContactsSection(props: ContactsSectionProps) -> Element {
|
||||||
debug!("ContactsSection rendering");
|
debug!("ContactsSection rendering");
|
||||||
|
|
||||||
let rooms_atom_ref = use_atom_ref(cx, &ROOMS);
|
let contacts = props.filter.to_owned()(&ROOMS);
|
||||||
let chats_window_interface_ref = use_atom_ref(cx, &CHATS_WIN_INTERFACE);
|
|
||||||
|
|
||||||
let contacts = filter(rooms_atom_ref.clone());
|
|
||||||
let contacts_len = contacts.len();
|
let contacts_len = contacts.len();
|
||||||
|
|
||||||
let show = use_state(cx, || false);
|
let mut show = use_signal(|| false);
|
||||||
|
|
||||||
let classes = [
|
let classes = [
|
||||||
ClassName::SECTION,
|
ClassName::SECTION,
|
||||||
if **show { ClassName::ACTIVE } else { "" },
|
if *show.read() { ClassName::ACTIVE } else { "" },
|
||||||
]
|
]
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
@@ -102,40 +112,44 @@ pub fn ContactsSection<'a>(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
rsx!(li {
|
rsx! {
|
||||||
onclick: move |_| on_clicked_room(&room_id, chats_window_interface_ref),
|
li {
|
||||||
|
onclick: move |_| on_clicked_room(&room_id, &CHATS_WIN_INTERFACE),
|
||||||
img {
|
img {
|
||||||
src: "./images/status_online.png",
|
src: "./images/status_online.png",
|
||||||
},
|
},
|
||||||
p {
|
p {
|
||||||
formatted,
|
{formatted},
|
||||||
},
|
},
|
||||||
p {
|
p {
|
||||||
style: "color: darkgrey;",
|
style: "color: darkgrey;",
|
||||||
room_topic,
|
{room_topic},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: "{classes}",
|
class: "{classes}",
|
||||||
|
|
||||||
p {
|
p {
|
||||||
class: ClassName::HEADER,
|
class: ClassName::HEADER,
|
||||||
onclick: move |_| show.set(!show),
|
onclick: move |_| {
|
||||||
|
let state = *show.read();
|
||||||
|
show.set(!state)
|
||||||
|
},
|
||||||
|
|
||||||
ContactsArrow {},
|
ContactsArrow {},
|
||||||
|
|
||||||
format!("{name} ({contacts_len})"),
|
{format!("{} ({contacts_len})", props.name)},
|
||||||
},
|
},
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
rendered_contacts.into_iter(),
|
{rendered_contacts.into_iter()},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,11 +10,11 @@ use crate::components::contacts_window::user_infos::UserInfos;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/contacts_window/contacts_window.scss");
|
turf::style_sheet!("src/components/contacts_window/contacts_window.scss");
|
||||||
|
|
||||||
pub fn ContactsWindow(cx: Scope) -> Element {
|
pub fn ContactsWindow() -> Element {
|
||||||
debug!("ContactsWindow rendering");
|
debug!("ContactsWindow rendering");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::CONTACTS_WINDOW,
|
class: ClassName::CONTACTS_WINDOW,
|
||||||
@@ -94,5 +94,5 @@ pub fn ContactsWindow(cx: Scope) -> Element {
|
|||||||
class: ClassName::FOOTER,
|
class: ClassName::FOOTER,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
// use fermi::*;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
// use crate::base::APP_SETTINGS;
|
|
||||||
use crate::components::avatar_selector::AvatarSelector;
|
use crate::components::avatar_selector::AvatarSelector;
|
||||||
use crate::components::icons::DownArrowIcon;
|
use crate::components::icons::DownArrowIcon;
|
||||||
|
|
||||||
@@ -10,7 +8,7 @@ turf::style_sheet!("src/components/contacts_window/user_infos.scss");
|
|||||||
|
|
||||||
static MESSAGE_PLACEHOLDER: &str = "<Enter a personal message>";
|
static MESSAGE_PLACEHOLDER: &str = "<Enter a personal message>";
|
||||||
|
|
||||||
pub fn UserInfos(cx: Scope) -> Element {
|
pub fn UserInfos() -> Element {
|
||||||
debug!("UserInfos rendering");
|
debug!("UserInfos rendering");
|
||||||
|
|
||||||
// let app_settings = use_atom_ref(cx, &APP_SETTINGS);
|
// let app_settings = use_atom_ref(cx, &APP_SETTINGS);
|
||||||
@@ -26,6 +24,7 @@ pub fn UserInfos(cx: Scope) -> Element {
|
|||||||
|
|
||||||
// let mut user_info_option = None;
|
// let mut user_info_option = None;
|
||||||
let user_display_name_option: Option<bool> = None;
|
let user_display_name_option: Option<bool> = None;
|
||||||
|
let user_display_name = "AIE";
|
||||||
|
|
||||||
// let user_id_option = &store.user_id;
|
// let user_id_option = &store.user_id;
|
||||||
// if user_id_option.is_some() {
|
// if user_id_option.is_some() {
|
||||||
@@ -36,8 +35,8 @@ pub fn UserInfos(cx: Scope) -> Element {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::USER_INFO,
|
class: ClassName::USER_INFO,
|
||||||
@@ -67,11 +66,11 @@ pub fn UserInfos(cx: Scope) -> Element {
|
|||||||
class: ClassName::USER_MESSAGE,
|
class: ClassName::USER_MESSAGE,
|
||||||
p {
|
p {
|
||||||
// TODO: Handle user message
|
// TODO: Handle user message
|
||||||
MESSAGE_PLACEHOLDER,
|
{MESSAGE_PLACEHOLDER},
|
||||||
}
|
}
|
||||||
DownArrowIcon {},
|
DownArrowIcon {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,14 +2,13 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/header.scss");
|
turf::style_sheet!("src/components/header.scss");
|
||||||
|
|
||||||
pub fn Header(cx: Scope) -> Element {
|
pub fn Header() -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::ROOT,
|
class: ClassName::ROOT,
|
||||||
img {
|
img {
|
||||||
// src: "./assets/live-logo2.png"
|
|
||||||
src: "./images/logo-msn.png"
|
src: "./images/logo-msn.png"
|
||||||
}
|
}
|
||||||
svg {
|
svg {
|
||||||
@@ -22,5 +21,5 @@ pub fn Header(cx: Scope) -> Element {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,15 +8,15 @@ include!(concat!(env!("OUT_DIR"), "/style_vars.rs"));
|
|||||||
|
|
||||||
use style::{COLOR_PRIMARY_100, COLOR_TERNARY_100};
|
use style::{COLOR_PRIMARY_100, COLOR_TERNARY_100};
|
||||||
|
|
||||||
pub fn DownArrowIcon(cx: Scope) -> Element {
|
pub fn DownArrowIcon() -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
class: ClassName::DOWN_ARROW_ICON,
|
class: ClassName::DOWN_ARROW_ICON,
|
||||||
icon: MdArrowDropDown,
|
icon: MdArrowDropDown,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _PYRAMID_OFFSET_X: f64 = 1.0;
|
const _PYRAMID_OFFSET_X: f64 = 1.0;
|
||||||
@@ -39,13 +39,14 @@ const _PYRAMID_CENTRAL_EDGE_Y_LEN: f64 = _PYRAMID_CENTRAL_EDGE_E2_Y - _PYRAMID_E
|
|||||||
const _PYRAMID_RIGHT_EDGE_E2_X: f64 = 130.0 + _PYRAMID_OFFSET_X;
|
const _PYRAMID_RIGHT_EDGE_E2_X: f64 = 130.0 + _PYRAMID_OFFSET_X;
|
||||||
const _PYRAMID_RIGHT_EDGE_E2_Y: f64 = _PYRAMID_LEFT_EDGE_E2_Y;
|
const _PYRAMID_RIGHT_EDGE_E2_Y: f64 = _PYRAMID_LEFT_EDGE_E2_Y;
|
||||||
|
|
||||||
struct PyramidShape<'a> {
|
#[derive(PartialEq, Clone)]
|
||||||
color: &'a str,
|
struct PyramidShape {
|
||||||
|
color: String,
|
||||||
ratio: f64,
|
ratio: f64,
|
||||||
progress_color: &'a str,
|
progress_color: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IconShape for PyramidShape<'a> {
|
impl IconShape for PyramidShape {
|
||||||
fn view_box(&self) -> String {
|
fn view_box(&self) -> String {
|
||||||
let height = _PYRAMID_CENTRAL_EDGE_E2_Y + _PYRAMID_STROKE_WIDTH;
|
let height = _PYRAMID_CENTRAL_EDGE_E2_Y + _PYRAMID_STROKE_WIDTH;
|
||||||
let width = _PYRAMID_RIGHT_EDGE_E2_X + _PYRAMID_STROKE_WIDTH;
|
let width = _PYRAMID_RIGHT_EDGE_E2_X + _PYRAMID_STROKE_WIDTH;
|
||||||
@@ -56,7 +57,7 @@ impl<'a> IconShape for PyramidShape<'a> {
|
|||||||
String::from("http://www.w3.org/2000/svg")
|
String::from("http://www.w3.org/2000/svg")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_elements(&self) -> LazyNodes {
|
fn child_elements(&self) -> Element {
|
||||||
let inverted_ratio = 1.0 - self.ratio;
|
let inverted_ratio = 1.0 - self.ratio;
|
||||||
|
|
||||||
let central_edge_ratio_e2_y =
|
let central_edge_ratio_e2_y =
|
||||||
@@ -107,24 +108,26 @@ impl<'a> IconShape for PyramidShape<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(PartialEq, Clone, Props)]
|
||||||
pub struct PyramidProps<'a> {
|
pub struct PyramidProps {
|
||||||
|
color: Option<String>,
|
||||||
#[props(default = 0.5)]
|
#[props(default = 0.5)]
|
||||||
color: Option<&'a str>,
|
|
||||||
ratio: f64,
|
ratio: f64,
|
||||||
progress_color: Option<&'a str>,
|
progress_color: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Pyramid<'a>(cx: Scope<'a, PyramidProps<'a>>) -> Element<'a> {
|
pub fn Pyramid(props: PyramidProps) -> Element {
|
||||||
let progress_color = cx.props.progress_color.unwrap_or(COLOR_PRIMARY_100);
|
let color = props.color.unwrap_or(COLOR_PRIMARY_100.to_string());
|
||||||
let color = cx.props.color.unwrap_or(COLOR_TERNARY_100);
|
let progress_color = props
|
||||||
|
.progress_color
|
||||||
|
.unwrap_or(COLOR_TERNARY_100.to_string());
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
class: ClassName::PYRAMID_ICON,
|
class: ClassName::PYRAMID_ICON,
|
||||||
icon: PyramidShape { ratio: cx.props.ratio, color, progress_color },
|
icon: PyramidShape { ratio: props.ratio, color, progress_color },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@@ -6,12 +6,11 @@ use super::wallpaper::Wallpaper;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/loading.scss");
|
turf::style_sheet!("src/components/loading.scss");
|
||||||
|
|
||||||
#[component]
|
pub fn LoadingPage() -> Element {
|
||||||
pub fn LoadingPage(cx: Scope) -> Element {
|
|
||||||
debug!("LoadingPage rendering");
|
debug!("LoadingPage rendering");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::LOADING,
|
class: ClassName::LOADING,
|
||||||
@@ -23,5 +22,5 @@ pub fn LoadingPage(cx: Scope) -> Element {
|
|||||||
Spinner {},
|
Spinner {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use fermi::*;
|
|
||||||
use rand::distributions::{Alphanumeric, DistString};
|
use rand::distributions::{Alphanumeric, DistString};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
use validator::{Validate, ValidateArgs, ValidateEmail, ValidationError, ValidationErrors};
|
use validator::{Validate, ValidateArgs, ValidateEmail, ValidationError, ValidationErrors};
|
||||||
@@ -46,7 +47,7 @@ const SHAPE_2_COLORS_STR: &str = formatcp!(
|
|||||||
const SHAPE_3_COLORS_STR: &str = formatcp!(
|
const SHAPE_3_COLORS_STR: &str = formatcp!(
|
||||||
"{COLOR_TERNARY_120},{COLOR_TERNARY_110},{COLOR_TERNARY_100},{COLOR_TERNARY_90},{COLOR_TERNARY_80}");
|
"{COLOR_TERNARY_120},{COLOR_TERNARY_110},{COLOR_TERNARY_100},{COLOR_TERNARY_90},{COLOR_TERNARY_80}");
|
||||||
|
|
||||||
async fn generate_random_avatar(url: &String) -> Option<String> {
|
async fn generate_random_avatar(url: String) -> Option<String> {
|
||||||
let seed = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
let seed = Alphanumeric.sample_string(&mut rand::thread_rng(), 16);
|
||||||
let req = format!(
|
let req = format!(
|
||||||
"https://{url}/7.x/shapes/svg?\
|
"https://{url}/7.x/shapes/svg?\
|
||||||
@@ -105,7 +106,7 @@ enum Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait OnValidationError {
|
trait OnValidationError {
|
||||||
fn on_validation_error(&self, error: &ValidationError) {
|
fn on_validation_error(&mut self, error: &ValidationError) {
|
||||||
let code = error.code.to_string();
|
let code = error.code.to_string();
|
||||||
let msg = match code.as_str() {
|
let msg = match code.as_str() {
|
||||||
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
||||||
@@ -115,8 +116,8 @@ trait OnValidationError {
|
|||||||
self.invalidate(msg.to_string());
|
self.invalidate(msg.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn reset(&self);
|
fn reset(&mut self);
|
||||||
fn invalidate(&self, helper_text: String);
|
fn invalidate(&mut self, helper_text: String);
|
||||||
fn box_clone(&self) -> Box<dyn OnValidationError>;
|
fn box_clone(&self) -> Box<dyn OnValidationError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,18 +129,18 @@ impl Clone for Box<dyn OnValidationError> {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TextInputHandler {
|
struct TextInputHandler {
|
||||||
state_ref: UseRef<TextInputState>,
|
state: Signal<TextInputState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextInputHandler {}
|
impl TextInputHandler {}
|
||||||
|
|
||||||
impl OnValidationError for TextInputHandler {
|
impl OnValidationError for TextInputHandler {
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.state_ref.write().reset();
|
self.state.write().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&self, helper_text: String) {
|
fn invalidate(&mut self, helper_text: String) {
|
||||||
self.state_ref.write().invalidate(helper_text);
|
self.state.write().invalidate(helper_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
||||||
@@ -149,17 +150,17 @@ impl OnValidationError for TextInputHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct UrlInputHandler {
|
struct UrlInputHandler {
|
||||||
state_ref: UseRef<TextInputState>,
|
state: Signal<TextInputState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UrlInputHandler {
|
impl UrlInputHandler {
|
||||||
pub fn new(state_ref: UseRef<TextInputState>) -> Self {
|
pub fn new(state: Signal<TextInputState>) -> Self {
|
||||||
Self { state_ref }
|
Self { state }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnValidationError for UrlInputHandler {
|
impl OnValidationError for UrlInputHandler {
|
||||||
fn on_validation_error(&self, error: &ValidationError) {
|
fn on_validation_error(&mut self, error: &ValidationError) {
|
||||||
let code = error.code.to_string();
|
let code = error.code.to_string();
|
||||||
let msg = match code.as_str() {
|
let msg = match code.as_str() {
|
||||||
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
||||||
@@ -171,12 +172,12 @@ impl OnValidationError for UrlInputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.state_ref.write().reset();
|
self.state.write().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&self, helper_text: String) {
|
fn invalidate(&mut self, helper_text: String) {
|
||||||
self.state_ref.write().invalidate(helper_text);
|
self.state.write().invalidate(helper_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
||||||
@@ -186,17 +187,17 @@ impl OnValidationError for UrlInputHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct EmailInputHandler {
|
struct EmailInputHandler {
|
||||||
state_ref: UseRef<TextInputState>,
|
state: Signal<TextInputState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EmailInputHandler {
|
impl EmailInputHandler {
|
||||||
pub fn new(state_ref: UseRef<TextInputState>) -> Self {
|
pub fn new(state: Signal<TextInputState>) -> Self {
|
||||||
Self { state_ref }
|
Self { state }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnValidationError for EmailInputHandler {
|
impl OnValidationError for EmailInputHandler {
|
||||||
fn on_validation_error(&self, error: &ValidationError) {
|
fn on_validation_error(&mut self, error: &ValidationError) {
|
||||||
let code = error.code.to_string();
|
let code = error.code.to_string();
|
||||||
let msg = match code.as_str() {
|
let msg = match code.as_str() {
|
||||||
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
||||||
@@ -207,12 +208,12 @@ impl OnValidationError for EmailInputHandler {
|
|||||||
self.invalidate(msg.to_string());
|
self.invalidate(msg.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.state_ref.write().reset();
|
self.state.write().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&self, helper_text: String) {
|
fn invalidate(&mut self, helper_text: String) {
|
||||||
self.state_ref.write().invalidate(helper_text);
|
self.state.write().invalidate(helper_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
||||||
@@ -222,17 +223,17 @@ impl OnValidationError for EmailInputHandler {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PasswordInputHandler {
|
struct PasswordInputHandler {
|
||||||
state_ref: UseRef<PasswordInputState>,
|
state: Signal<PasswordInputState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PasswordInputHandler {
|
impl PasswordInputHandler {
|
||||||
pub fn new(state_ref: UseRef<PasswordInputState>) -> Self {
|
pub fn new(state: Signal<PasswordInputState>) -> Self {
|
||||||
Self { state_ref }
|
Self { state }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnValidationError for PasswordInputHandler {
|
impl OnValidationError for PasswordInputHandler {
|
||||||
fn on_validation_error(&self, error: &ValidationError) {
|
fn on_validation_error(&mut self, error: &ValidationError) {
|
||||||
let code = error.code.to_string();
|
let code = error.code.to_string();
|
||||||
let msg = match code.as_str() {
|
let msg = match code.as_str() {
|
||||||
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
REQUIRED_ERROR_NAME => Some(REQUIRED_ERROR_HELPER_TEXT),
|
||||||
@@ -244,7 +245,7 @@ impl OnValidationError for PasswordInputHandler {
|
|||||||
score = guesses_log10;
|
score = guesses_log10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.state_ref.write().score = score;
|
self.state.write().score = score;
|
||||||
Some(TOO_WEAK_PASSWORD_ERROR_HELPER_TEXT)
|
Some(TOO_WEAK_PASSWORD_ERROR_HELPER_TEXT)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -254,12 +255,12 @@ impl OnValidationError for PasswordInputHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&self) {
|
fn reset(&mut self) {
|
||||||
self.state_ref.write().reset();
|
self.state.write().reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&self, helper_text: String) {
|
fn invalidate(&mut self, helper_text: String) {
|
||||||
self.state_ref.write().invalidate(helper_text);
|
self.state.write().invalidate(helper_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
fn box_clone(&self) -> Box<dyn OnValidationError> {
|
||||||
@@ -273,7 +274,9 @@ fn on_validation_errors(
|
|||||||
) {
|
) {
|
||||||
for (field_name, errors) in field_errors {
|
for (field_name, errors) in field_errors {
|
||||||
if let Some(handler) = handlers.get(field_name) {
|
if let Some(handler) = handlers.get(field_name) {
|
||||||
errors.iter().for_each(|e| handler.on_validation_error(e));
|
errors
|
||||||
|
.iter()
|
||||||
|
.for_each(|e| handler.borrow_mut().on_validation_error(e));
|
||||||
} else if *field_name == "__all__" {
|
} else if *field_name == "__all__" {
|
||||||
for error in *errors {
|
for error in *errors {
|
||||||
let code = error.code.to_string();
|
let code = error.code.to_string();
|
||||||
@@ -307,7 +310,7 @@ fn on_validation_errors(
|
|||||||
if other_field_names_len > 1 { "s" } else { "" },
|
if other_field_names_len > 1 { "s" } else { "" },
|
||||||
);
|
);
|
||||||
|
|
||||||
handler.invalidate(formatted);
|
handler.borrow_mut().invalidate(formatted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,7 +322,7 @@ fn on_validation_errors(
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Some(handler) = handlers.get(field_name) {
|
if let Some(handler) = handlers.get(field_name) {
|
||||||
handler.on_validation_error(error);
|
handler.borrow_mut().on_validation_error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
other => todo!("{:?}", other),
|
other => todo!("{:?}", other),
|
||||||
@@ -463,18 +466,15 @@ fn validate_password(password: &Option<String>, process: &Process) -> Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_login(
|
fn on_login(session: &GlobalSignal<Session>, data: Signal<Data>) -> Result<(), ValidationErrors> {
|
||||||
session_ref: &UseAtomRef<Session>,
|
let data = data.read();
|
||||||
data_ref: &UseRef<Data>,
|
|
||||||
) -> Result<(), ValidationErrors> {
|
|
||||||
let login = data_ref.read();
|
|
||||||
|
|
||||||
match login.validate_with_args(&Process::Login) {
|
match data.validate_with_args(&Process::Login) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
session_ref.write().update(
|
session.write().update(
|
||||||
login.homeserver_url.clone(),
|
data.homeserver_url.clone(),
|
||||||
login.id.clone(),
|
data.id.clone(),
|
||||||
login.password.clone(),
|
data.password.clone(),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -483,12 +483,12 @@ fn on_login(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_register(
|
fn on_register(
|
||||||
_session_ref: &UseAtomRef<Session>,
|
_session: &GlobalSignal<Session>,
|
||||||
data_ref: &UseRef<Data>,
|
data: Signal<Data>,
|
||||||
) -> Result<(), ValidationErrors> {
|
) -> Result<(), ValidationErrors> {
|
||||||
let login = data_ref.read();
|
let data = data.read();
|
||||||
|
|
||||||
match login.validate_with_args(&Process::Registration) {
|
match data.validate_with_args(&Process::Registration) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
error!("TODO: Manage registration process");
|
error!("TODO: Manage registration process");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -499,8 +499,9 @@ fn on_register(
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct InputHandlers<'a> {
|
struct InputHandlers<'a> {
|
||||||
handlers: HashMap<&'a str, Box<dyn OnValidationError>>,
|
handlers: HashMap<&'a str, Rc<RefCell<dyn OnValidationError>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> InputHandlers<'a> {
|
impl<'a> InputHandlers<'a> {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -508,28 +509,24 @@ impl<'a> InputHandlers<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn insert<T: 'static + OnValidationError>(&mut self, name: &'a str, handler: T) {
|
fn insert<T: 'static + OnValidationError>(&mut self, name: &'a str, handler: T) {
|
||||||
self.handlers.insert(name, Box::new(handler));
|
self.handlers.insert(name, Rc::new(RefCell::new(handler)));
|
||||||
}
|
}
|
||||||
|
fn get(&self, name: &'a str) -> Option<&Rc<RefCell<(dyn OnValidationError + 'static)>>> {
|
||||||
fn get(&self, name: &'a str) -> Option<&dyn OnValidationError> {
|
|
||||||
if let Some(handler) = self.handlers.get(name) {
|
if let Some(handler) = self.handlers.get(name) {
|
||||||
return Some(handler.as_ref());
|
return Some(handler);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
fn reset_handlers(&self) {
|
fn reset_handlers(&self) {
|
||||||
self.handlers.values().for_each(|h| h.reset());
|
self.handlers.values().for_each(|h| h.borrow_mut().reset());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! on_input {
|
macro_rules! on_input {
|
||||||
($data_ref:ident, $data_field:ident) => {
|
($data_ref:ident, $data_field:ident) => {
|
||||||
move |evt: FormEvent| {
|
move |evt: FormEvent| {
|
||||||
$data_ref.write().$data_field = if evt.value.len() > 0 {
|
let value = evt.value();
|
||||||
Some(evt.value.clone())
|
$data_ref.write().$data_field = if !value.is_empty() { Some(value) } else { None }
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -537,43 +534,40 @@ macro_rules! on_input {
|
|||||||
macro_rules! refresh_password_state {
|
macro_rules! refresh_password_state {
|
||||||
($data:ident, $data_field:ident, $state:ident) => {
|
($data:ident, $data_field:ident, $state:ident) => {
|
||||||
let mut rating = 0.0;
|
let mut rating = 0.0;
|
||||||
if let Some(password) = &$data.$data_field {
|
if let Some(password) = &$data.peek().$data_field {
|
||||||
if let Some(result) = compute_password_score(password, None) {
|
if let Some(result) = compute_password_score(password, None) {
|
||||||
rating = result.rating;
|
rating = result.rating;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$state.write_silent().score = rating;
|
$state.write().score = rating;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_modal<'a, 'b>(
|
fn generate_modal(
|
||||||
config: &'b PasswordSuggestionsModalConfig<'a>,
|
config: &PasswordSuggestionsModalConfig,
|
||||||
on_confirm: impl Fn(Event<MouseData>) + 'b,
|
on_confirm: impl FnMut(Event<MouseData>) + 'static,
|
||||||
) -> LazyNodes<'a, 'b>
|
) -> Element {
|
||||||
where
|
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
let suggestions = config.suggestions.get(PASSWORD_FIELD_NAME);
|
let suggestions = config.suggestions.get(PASSWORD_FIELD_NAME);
|
||||||
|
|
||||||
let mut rendered_suggestions = Vec::<LazyNodes>::new();
|
let mut rendered_suggestions = Vec::<Element>::new();
|
||||||
if let Some(suggestions) = suggestions {
|
if let Some(suggestions) = suggestions {
|
||||||
if suggestions.len() == 1 {
|
if suggestions.len() == 1 {
|
||||||
rendered_suggestions.push(rsx!(suggestions[0].as_str()));
|
rendered_suggestions.push(rsx!({ suggestions[0].as_str() }));
|
||||||
} else {
|
} else {
|
||||||
suggestions
|
suggestions
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|s| rendered_suggestions.push(rsx!(li { s.as_str() })));
|
.for_each(|s| rendered_suggestions.push(rsx!(li { {s.as_str()} })));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
Modal {
|
Modal {
|
||||||
severity: config.severity.clone(),
|
severity: config.severity,
|
||||||
title: config.title.as_ref(),
|
title: config.title.as_ref(),
|
||||||
on_confirm: on_confirm,
|
on_confirm: on_confirm,
|
||||||
|
|
||||||
div {
|
div {
|
||||||
rendered_suggestions.into_iter()
|
{rendered_suggestions.into_iter()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -597,70 +591,64 @@ impl<'a> PasswordSuggestionsModalConfig<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct LoginProps<'a> {
|
pub struct LoginProps {
|
||||||
dicebear_hostname: Option<&'a str>,
|
dicebear_hostname: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
pub fn Login(props: LoginProps) -> Element {
|
||||||
debug!("Login rendering");
|
debug!("Login rendering");
|
||||||
|
|
||||||
let session = use_atom_ref(cx, &SESSION);
|
let mut data = use_signal(Data::new);
|
||||||
|
|
||||||
let data_ref = use_ref(cx, Data::new);
|
let mut current_process = use_signal(|| Process::Login);
|
||||||
|
|
||||||
let current_process = use_state(cx, || Process::Login);
|
let data_lock = data.read();
|
||||||
|
|
||||||
let data = data_ref.read();
|
let homeserver_url = data_lock.homeserver_url.as_deref().unwrap_or("");
|
||||||
|
let homeserver_url_state = use_signal(TextInputState::new);
|
||||||
let homeserver_url = data.homeserver_url.as_deref().unwrap_or("");
|
let id = data_lock.id.as_deref().unwrap_or("");
|
||||||
let homeserver_url_state = use_ref(cx, TextInputState::new);
|
let id_state = use_signal(TextInputState::new);
|
||||||
let id = data.id.as_deref().unwrap_or("");
|
let password = data_lock.password.as_deref().unwrap_or("");
|
||||||
let id_state = use_ref(cx, TextInputState::new);
|
let mut password_state = use_signal(PasswordInputState::new);
|
||||||
let password = data.password.as_deref().unwrap_or("");
|
let confirm_password = data_lock.confirm_password.as_deref().unwrap_or("");
|
||||||
let password_state = use_ref(cx, PasswordInputState::new);
|
let mut confirm_password_state = use_signal(PasswordInputState::new);
|
||||||
let confirm_password = data.confirm_password.as_deref().unwrap_or("");
|
|
||||||
let confirm_password_state = use_ref(cx, PasswordInputState::new);
|
|
||||||
|
|
||||||
let mut handlers = InputHandlers::new();
|
let mut handlers = InputHandlers::new();
|
||||||
handlers.insert(
|
handlers.insert(
|
||||||
HOMESERVER_FIELD_NAME,
|
HOMESERVER_FIELD_NAME,
|
||||||
UrlInputHandler::new(homeserver_url_state.clone()),
|
UrlInputHandler::new(homeserver_url_state),
|
||||||
);
|
);
|
||||||
handlers.insert(ID_FIELD_NAME, EmailInputHandler::new(id_state.clone()));
|
handlers.insert(ID_FIELD_NAME, EmailInputHandler::new(id_state));
|
||||||
handlers.insert(
|
handlers.insert(
|
||||||
PASSWORD_FIELD_NAME,
|
PASSWORD_FIELD_NAME,
|
||||||
PasswordInputHandler::new(password_state.clone()),
|
PasswordInputHandler::new(password_state),
|
||||||
);
|
);
|
||||||
handlers.insert(
|
handlers.insert(
|
||||||
CONFIRM_PASSWORD_FIELD_NAME,
|
CONFIRM_PASSWORD_FIELD_NAME,
|
||||||
PasswordInputHandler::new(confirm_password_state.clone()),
|
PasswordInputHandler::new(confirm_password_state),
|
||||||
);
|
);
|
||||||
|
|
||||||
let spinner_animated = use_state(cx, || false);
|
let mut spinner_animated = use_signal(|| false);
|
||||||
let id_placeholder = use_state(cx, || LOGIN_ID_PLACEHOLDER);
|
let mut id_placeholder = use_signal(|| LOGIN_ID_PLACEHOLDER);
|
||||||
|
|
||||||
let url = cx
|
let url = props
|
||||||
.props
|
|
||||||
.dicebear_hostname
|
.dicebear_hostname
|
||||||
.unwrap_or("dicebear.tools.adrien.run")
|
.unwrap_or("dicebear.tools.adrien.run".to_string());
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let random_avatar_future =
|
let mut random_avatar_future = use_resource(move || {
|
||||||
use_future(
|
to_owned![url];
|
||||||
cx,
|
async move { generate_random_avatar(url).await }
|
||||||
&url,
|
});
|
||||||
|url| async move { generate_random_avatar(&url).await },
|
|
||||||
);
|
|
||||||
|
|
||||||
let avatar = match random_avatar_future.value() {
|
let avatar = match &*random_avatar_future.read_unchecked() {
|
||||||
Some(Some(svg)) => {
|
Some(Some(svg)) => {
|
||||||
rsx!(div {
|
rsx!(div {
|
||||||
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
||||||
dangerous_inner_html: svg.as_str(),
|
dangerous_inner_html: svg.as_str(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(None) | None => {
|
Some(None) => {
|
||||||
warn!("No profile image set or generated, display the placeholder");
|
warn!("No profile image set or generated, display the placeholder");
|
||||||
rsx!(div {
|
rsx!(div {
|
||||||
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
class: ClassName::LOGIN_FORM_PHOTO_CONTENT,
|
||||||
@@ -669,9 +657,10 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if **spinner_animated && session.read().is_logged {
|
if *spinner_animated.read() && SESSION.read().is_logged {
|
||||||
debug!("Stop spinner");
|
debug!("Stop spinner");
|
||||||
spinner_animated.set(false);
|
spinner_animated.set(false);
|
||||||
}
|
}
|
||||||
@@ -679,11 +668,11 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
refresh_password_state!(data, password, password_state);
|
refresh_password_state!(data, password, password_state);
|
||||||
refresh_password_state!(data, confirm_password, confirm_password_state);
|
refresh_password_state!(data, confirm_password, confirm_password_state);
|
||||||
|
|
||||||
let modal_configs = use_ref(cx, Vec::<PasswordSuggestionsModalConfig>::new);
|
let mut modal_configs = use_signal(Vec::<PasswordSuggestionsModalConfig>::new);
|
||||||
let modal_config = use_state(cx, || None::<PasswordSuggestionsModalConfig>);
|
let mut modal_config = use_signal(|| None::<PasswordSuggestionsModalConfig>);
|
||||||
|
|
||||||
if modal_configs.read().len() > 0 && modal_config.is_none() {
|
if !modal_configs.read().is_empty() && modal_config.read().is_none() {
|
||||||
modal_config.set(modal_configs.write_silent().pop());
|
modal_config.set(modal_configs.write().pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
let on_clicked_login = {
|
let on_clicked_login = {
|
||||||
@@ -693,15 +682,15 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
|
|
||||||
handlers.reset_handlers();
|
handlers.reset_handlers();
|
||||||
|
|
||||||
if **current_process == Process::Registration {
|
if *current_process.read() == Process::Registration {
|
||||||
current_process.set(Process::Login);
|
current_process.set(Process::Login);
|
||||||
data_ref.write().id = None;
|
data.write().id = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spinner_animated.set(true);
|
spinner_animated.set(true);
|
||||||
|
|
||||||
if let Err(errors) = on_login(session, data_ref) {
|
if let Err(errors) = on_login(&SESSION, data) {
|
||||||
let field_errors = errors.field_errors();
|
let field_errors = errors.field_errors();
|
||||||
on_validation_errors(&field_errors, &handlers);
|
on_validation_errors(&field_errors, &handlers);
|
||||||
}
|
}
|
||||||
@@ -714,9 +703,9 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
to_owned![handlers, modal_configs];
|
to_owned![handlers, modal_configs];
|
||||||
|
|
||||||
move |_| {
|
move |_| {
|
||||||
if **current_process == Process::Login {
|
if *current_process.read() == Process::Login {
|
||||||
current_process.set(Process::Registration);
|
current_process.set(Process::Registration);
|
||||||
data_ref.write().id = None;
|
data.write().id = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +713,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
|
|
||||||
spinner_animated.set(true);
|
spinner_animated.set(true);
|
||||||
|
|
||||||
if let Err(errors) = on_register(session, data_ref) {
|
if let Err(errors) = on_register(&SESSION, data) {
|
||||||
let field_name = PASSWORD_FIELD_NAME;
|
let field_name = PASSWORD_FIELD_NAME;
|
||||||
|
|
||||||
let field_errors = errors.field_errors();
|
let field_errors = errors.field_errors();
|
||||||
@@ -770,18 +759,18 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
let mut password_classes: [&str; 2] = [ClassName::LOGIN_FORM_PASSWORD, ""];
|
let mut password_classes: [&str; 2] = [ClassName::LOGIN_FORM_PASSWORD, ""];
|
||||||
let mut confirm_password_classes: [&str; 2] = [ClassName::LOGIN_FORM_CONFIRM_PASSWORD, ""];
|
let mut confirm_password_classes: [&str; 2] = [ClassName::LOGIN_FORM_CONFIRM_PASSWORD, ""];
|
||||||
|
|
||||||
match **current_process {
|
match *current_process.read() {
|
||||||
Process::Registration => {
|
Process::Registration => {
|
||||||
form_classes[1] = ClassName::REGISTER;
|
form_classes[1] = ClassName::REGISTER;
|
||||||
password_classes[1] = ClassName::SHOW;
|
password_classes[1] = ClassName::SHOW;
|
||||||
confirm_password_classes[1] = ClassName::SHOW;
|
confirm_password_classes[1] = ClassName::SHOW;
|
||||||
|
|
||||||
if **id_placeholder != REGISTER_ID_PLACEHOLDER {
|
if *id_placeholder.read() != REGISTER_ID_PLACEHOLDER {
|
||||||
id_placeholder.set(REGISTER_ID_PLACEHOLDER);
|
id_placeholder.set(REGISTER_ID_PLACEHOLDER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Process::Login => {
|
Process::Login => {
|
||||||
if **id_placeholder != LOGIN_ID_PLACEHOLDER {
|
if *id_placeholder.read() != LOGIN_ID_PLACEHOLDER {
|
||||||
id_placeholder.set(LOGIN_ID_PLACEHOLDER);
|
id_placeholder.set(LOGIN_ID_PLACEHOLDER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -790,17 +779,18 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
let on_modal_confirm = move |_: Event<MouseData>| {
|
let on_modal_confirm = move |_: Event<MouseData>| {
|
||||||
modal_config.set(None);
|
modal_config.set(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
let rendered_modal = modal_config
|
let rendered_modal = modal_config
|
||||||
.get()
|
.read()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|modal_config| render!(generate_modal(modal_config, on_modal_confirm)));
|
.map(|modal_config| rsx!({ generate_modal(modal_config, on_modal_confirm) }));
|
||||||
|
|
||||||
let form_classes_str = form_classes.join(" ");
|
let form_classes_str = form_classes.join(" ");
|
||||||
let password_classes_str = password_classes.join(" ");
|
let password_classes_str = password_classes.join(" ");
|
||||||
let confirm_password_classes_str = confirm_password_classes.join(" ");
|
let confirm_password_classes_str = confirm_password_classes.join(" ");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
Wallpaper {},
|
Wallpaper {},
|
||||||
|
|
||||||
@@ -826,7 +816,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
placeholder: "Homeserver URL",
|
placeholder: "Homeserver URL",
|
||||||
value: "{homeserver_url}",
|
value: "{homeserver_url}",
|
||||||
state: homeserver_url_state,
|
state: homeserver_url_state,
|
||||||
oninput: on_input![data_ref, homeserver_url],
|
oninput: on_input![data, homeserver_url],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -836,7 +826,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
placeholder: "{id_placeholder}",
|
placeholder: "{id_placeholder}",
|
||||||
value: "{id}",
|
value: "{id}",
|
||||||
state: id_state,
|
state: id_state,
|
||||||
oninput: on_input![data_ref, id],
|
oninput: on_input![data, id],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -846,7 +836,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
placeholder: "Password",
|
placeholder: "Password",
|
||||||
value: "{password}",
|
value: "{password}",
|
||||||
state: password_state,
|
state: password_state,
|
||||||
oninput: on_input![data_ref, password],
|
oninput: on_input![data, password],
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
@@ -857,14 +847,14 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
placeholder: "Confirm Password",
|
placeholder: "Confirm Password",
|
||||||
value: "{confirm_password}",
|
value: "{confirm_password}",
|
||||||
state: confirm_password_state,
|
state: confirm_password_state,
|
||||||
oninput: on_input![data_ref, confirm_password],
|
oninput: on_input![data, confirm_password],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::LOGIN_FORM_SPINNER,
|
class: ClassName::LOGIN_FORM_SPINNER,
|
||||||
Spinner {
|
Spinner {
|
||||||
animate: **spinner_animated,
|
animate: *spinner_animated.read(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -885,6 +875,6 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps>) -> Element<'a> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
rendered_modal,
|
{rendered_modal},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,19 +1,15 @@
|
|||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use fermi::*;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::base::SESSION;
|
use crate::base::SESSION;
|
||||||
use crate::components::contacts_window::ContactsWindow;
|
use crate::components::contacts_window::ContactsWindow;
|
||||||
|
|
||||||
pub fn MainWindow(cx: Scope) -> Element {
|
pub fn MainWindow() -> Element {
|
||||||
debug!("MainWindow rendering");
|
debug!("MainWindow rendering");
|
||||||
|
|
||||||
let session_ref = use_atom_ref(cx, &SESSION);
|
rsx! {
|
||||||
let is_logged = session_ref.read().is_logged;
|
if SESSION.read().is_logged {
|
||||||
|
ContactsWindow {}
|
||||||
cx.render(rsx! {
|
}
|
||||||
if is_logged {
|
|
||||||
rsx!(ContactsWindow {})
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@ use style::{COLOR_CRITICAL_100, COLOR_SUCCESS_100, COLOR_WARNING_100};
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/modal.scss");
|
turf::style_sheet!("src/components/modal.scss");
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
Ok,
|
Ok,
|
||||||
Warning,
|
Warning,
|
||||||
@@ -39,14 +39,14 @@ struct DicebearConfig<'a> {
|
|||||||
lips: Vec<u32>,
|
lips: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct ModalProps<'a> {
|
pub struct ModalProps {
|
||||||
pub severity: Severity,
|
pub severity: Severity,
|
||||||
#[props(optional)]
|
#[props(optional)]
|
||||||
pub title: Option<&'a str>,
|
pub title: Option<String>,
|
||||||
pub children: Element<'a>,
|
pub children: Element,
|
||||||
#[props(optional)]
|
#[props(optional)]
|
||||||
pub on_confirm: Option<EventHandler<'a, MouseEvent>>,
|
pub on_confirm: Option<EventHandler<MouseEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dicebear_variants() -> &'static HashMap<Severity, DicebearConfig<'static>> {
|
fn dicebear_variants() -> &'static HashMap<Severity, DicebearConfig<'static>> {
|
||||||
@@ -96,10 +96,10 @@ fn render_dicebear_variants(values: &[u32]) -> String {
|
|||||||
.join(",")
|
.join(",")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn generate_random_figure(url: &String, severity: Severity) -> Option<String> {
|
async fn generate_random_figure(url: &str, severity: &Severity) -> Option<String> {
|
||||||
let mut res: Option<String> = None;
|
let mut res: Option<String> = None;
|
||||||
|
|
||||||
let config = match dicebear_variants().get(&severity) {
|
let config = match dicebear_variants().get(severity) {
|
||||||
Some(config) => config,
|
Some(config) => config,
|
||||||
None => {
|
None => {
|
||||||
error!("No dicebear configuration found for \"{severity}\"");
|
error!("No dicebear configuration found for \"{severity}\"");
|
||||||
@@ -144,17 +144,14 @@ async fn generate_random_figure(url: &String, severity: Severity) -> Option<Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
pub fn Modal(props: ModalProps) -> Element {
|
||||||
// TODO: Use configuration file
|
// TODO: Use configuration file
|
||||||
let url = "dicebear.tools.adrien.run".to_string();
|
let url = "dicebear.tools.adrien.run";
|
||||||
|
|
||||||
let severity = cx.props.severity.clone();
|
let random_figure_future =
|
||||||
|
use_resource(move || async move { generate_random_figure(url, &props.severity).await });
|
||||||
|
|
||||||
let random_figure_future = use_future(cx, &url, |url| async move {
|
let figure = match &*random_figure_future.read_unchecked() {
|
||||||
generate_random_figure(&url, severity).await
|
|
||||||
});
|
|
||||||
|
|
||||||
let figure = match random_figure_future.value() {
|
|
||||||
Some(Some(svg)) => Some(rsx! {
|
Some(Some(svg)) => Some(rsx! {
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
class: ClassName::MODAL_CONTENT_ICON_PLACEHOLDER,
|
||||||
@@ -163,7 +160,7 @@ pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
|||||||
}),
|
}),
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
warn!("No profile image set or generated, display the placeholder");
|
warn!("No profile image set or generated, display the placeholder");
|
||||||
let path = match cx.props.severity {
|
let path = match &props.severity {
|
||||||
Severity::Ok => "./images/modal-default-ok-icon.svg",
|
Severity::Ok => "./images/modal-default-ok-icon.svg",
|
||||||
Severity::Warning => "./images/modal-default-warning-icon.svg",
|
Severity::Warning => "./images/modal-default-warning-icon.svg",
|
||||||
Severity::Critical => "./images/modal-default-critical-icon.svg",
|
Severity::Critical => "./images/modal-default-critical-icon.svg",
|
||||||
@@ -180,7 +177,7 @@ pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let button_class = match cx.props.severity {
|
let button_class = match &props.severity {
|
||||||
Severity::Ok => SuccessButton,
|
Severity::Ok => SuccessButton,
|
||||||
Severity::Warning => WarningButton,
|
Severity::Warning => WarningButton,
|
||||||
Severity::Critical => ErrorButton,
|
Severity::Critical => ErrorButton,
|
||||||
@@ -188,8 +185,8 @@ pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
|||||||
|
|
||||||
figure.as_ref()?;
|
figure.as_ref()?;
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL,
|
class: ClassName::MODAL,
|
||||||
@@ -204,20 +201,19 @@ pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
|||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_TITLE,
|
class: ClassName::MODAL_CONTENT_TITLE,
|
||||||
cx.props.title,
|
{props.title},
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_MSG,
|
class: ClassName::MODAL_CONTENT_MSG,
|
||||||
|
{props.children},
|
||||||
&cx.props.children,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::MODAL_CONTENT_BUTTONS,
|
class: ClassName::MODAL_CONTENT_BUTTONS,
|
||||||
button_class {
|
button_class {
|
||||||
onclick: move |evt| {
|
onclick: move |evt| {
|
||||||
if let Some(cb) = &cx.props.on_confirm {
|
if let Some(cb) = &props.on_confirm {
|
||||||
cb.call(evt);
|
cb.call(evt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -225,5 +221,5 @@ pub fn Modal<'a>(cx: Scope<'a, ModalProps<'a>>) -> Element<'a> {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@ use dioxus_free_icons::{Icon, IconShape};
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/spinner.scss");
|
turf::style_sheet!("src/components/spinner.scss");
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Clone, PartialEq)]
|
||||||
struct _Spinner;
|
struct _Spinner;
|
||||||
impl IconShape for _Spinner {
|
impl IconShape for _Spinner {
|
||||||
fn view_box(&self) -> String {
|
fn view_box(&self) -> String {
|
||||||
@@ -12,7 +12,7 @@ impl IconShape for _Spinner {
|
|||||||
fn xmlns(&self) -> String {
|
fn xmlns(&self) -> String {
|
||||||
String::from("http://www.w3.org/2000/svg")
|
String::from("http://www.w3.org/2000/svg")
|
||||||
}
|
}
|
||||||
fn child_elements(&self) -> LazyNodes {
|
fn child_elements(&self) -> Element {
|
||||||
rsx! {
|
rsx! {
|
||||||
path {
|
path {
|
||||||
"stroke-linejoin": "round",
|
"stroke-linejoin": "round",
|
||||||
@@ -23,23 +23,23 @@ impl IconShape for _Spinner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
#[derive(PartialEq, Clone, Props)]
|
||||||
pub struct SpinnerProps {
|
pub struct SpinnerProps {
|
||||||
#[props(default = true)]
|
#[props(default = true)]
|
||||||
animate: bool,
|
animate: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Spinner(cx: Scope<SpinnerProps>) -> Element {
|
pub fn Spinner(props: SpinnerProps) -> Element {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::SPINNER,
|
class: ClassName::SPINNER,
|
||||||
|
|
||||||
Icon {
|
Icon {
|
||||||
class: if cx.props.animate { "" } else { ClassName::PAUSED },
|
class: if props.animate { "" } else { ClassName::PAUSED },
|
||||||
icon: _Spinner,
|
icon: _Spinner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,15 +9,15 @@ turf::style_sheet!("src/components/text_input.scss");
|
|||||||
|
|
||||||
pub trait InputPropsData {}
|
pub trait InputPropsData {}
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct InputProps<'a, D: InputPropsData + 'a> {
|
pub struct InputProps<D: InputPropsData + 'static + std::cmp::PartialEq> {
|
||||||
value: Option<&'a str>,
|
value: Option<String>,
|
||||||
placeholder: Option<&'a str>,
|
placeholder: Option<String>,
|
||||||
oninput: Option<EventHandler<'a, Event<FormData>>>,
|
oninput: Option<EventHandler<Event<FormData>>>,
|
||||||
state: Option<&'a UseRef<D>>,
|
state: Option<Signal<D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct TextInputState {
|
pub struct TextInputState {
|
||||||
pub is_valid: bool,
|
pub is_valid: bool,
|
||||||
pub helper_text: Option<String>,
|
pub helper_text: Option<String>,
|
||||||
@@ -50,11 +50,11 @@ impl Default for TextInputState {
|
|||||||
|
|
||||||
impl InputPropsData for TextInputState {}
|
impl InputPropsData for TextInputState {}
|
||||||
|
|
||||||
pub fn TextInput<'a>(cx: Scope<'a, InputProps<'a, TextInputState>>) -> Element<'a> {
|
pub fn TextInput(props: InputProps<TextInputState>) -> Element {
|
||||||
let mut criticity_class = "";
|
let mut criticity_class = "";
|
||||||
let mut helper_text = "".to_string();
|
let mut helper_text = "".to_string();
|
||||||
|
|
||||||
if let Some(state) = cx.props.state {
|
if let Some(state) = props.state {
|
||||||
let state = state.read();
|
let state = state.read();
|
||||||
if !state.is_valid {
|
if !state.is_valid {
|
||||||
criticity_class = ClassName::INVALID;
|
criticity_class = ClassName::INVALID;
|
||||||
@@ -66,8 +66,8 @@ pub fn TextInput<'a>(cx: Scope<'a, InputProps<'a, TextInputState>>) -> Element<'
|
|||||||
|
|
||||||
let input_classes_str = [ClassName::TEXT_INPUT_INPUT, criticity_class].join(" ");
|
let input_classes_str = [ClassName::TEXT_INPUT_INPUT, criticity_class].join(" ");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::TEXT_INPUT,
|
class: ClassName::TEXT_INPUT,
|
||||||
@@ -75,11 +75,11 @@ pub fn TextInput<'a>(cx: Scope<'a, InputProps<'a, TextInputState>>) -> Element<'
|
|||||||
input {
|
input {
|
||||||
class: "{input_classes_str}",
|
class: "{input_classes_str}",
|
||||||
r#type: "text",
|
r#type: "text",
|
||||||
placeholder: cx.props.placeholder,
|
placeholder: props.placeholder,
|
||||||
value: cx.props.value,
|
value: props.value,
|
||||||
|
|
||||||
oninput: move |evt| {
|
oninput: move |evt| {
|
||||||
if let Some(cb) = &cx.props.oninput {
|
if let Some(cb) = &props.oninput {
|
||||||
cb.call(evt);
|
cb.call(evt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -90,14 +90,14 @@ pub fn TextInput<'a>(cx: Scope<'a, InputProps<'a, TextInputState>>) -> Element<'
|
|||||||
|
|
||||||
p {
|
p {
|
||||||
class: criticity_class,
|
class: criticity_class,
|
||||||
helper_text
|
{helper_text}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Props)]
|
#[derive(Props, Clone, PartialEq)]
|
||||||
pub struct PasswordInputState {
|
pub struct PasswordInputState {
|
||||||
text_input_state: TextInputState,
|
text_input_state: TextInputState,
|
||||||
#[props(default = 0.0)]
|
#[props(default = 0.0)]
|
||||||
@@ -129,14 +129,14 @@ impl Default for PasswordInputState {
|
|||||||
|
|
||||||
impl InputPropsData for PasswordInputState {}
|
impl InputPropsData for PasswordInputState {}
|
||||||
|
|
||||||
pub fn PasswordTextInput<'a>(cx: Scope<'a, InputProps<'a, PasswordInputState>>) -> Element<'a> {
|
pub fn PasswordTextInput(props: InputProps<PasswordInputState>) -> Element {
|
||||||
let mut criticity_class = "";
|
let mut criticity_class = "";
|
||||||
let mut helper_text: String = "".to_string();
|
let mut helper_text: String = "".to_string();
|
||||||
let mut score: Option<f64> = None;
|
let mut score: Option<f64> = None;
|
||||||
|
|
||||||
let show_password = use_state(cx, || false);
|
let mut show_password = use_signal(|| false);
|
||||||
|
|
||||||
if let Some(state) = cx.props.state {
|
if let Some(state) = props.state {
|
||||||
let state = state.read();
|
let state = state.read();
|
||||||
if !state.text_input_state.is_valid {
|
if !state.text_input_state.is_valid {
|
||||||
criticity_class = ClassName::INVALID;
|
criticity_class = ClassName::INVALID;
|
||||||
@@ -158,55 +158,50 @@ pub fn PasswordTextInput<'a>(cx: Scope<'a, InputProps<'a, PasswordInputState>>)
|
|||||||
.join(" ");
|
.join(" ");
|
||||||
let input_classes = [ClassName::PASSWORD_TEXT_INPUT_INPUT, criticity_class].join(" ");
|
let input_classes = [ClassName::PASSWORD_TEXT_INPUT_INPUT, criticity_class].join(" ");
|
||||||
|
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
style { STYLE_SHEET },
|
style { {STYLE_SHEET} },
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: "{text_input_classes}",
|
class: "{text_input_classes}",
|
||||||
|
|
||||||
input {
|
input {
|
||||||
class: "{input_classes}",
|
class: "{input_classes}",
|
||||||
r#type: if **show_password { "text" } else { "password" },
|
r#type: if *show_password.read() { "text" } else { "password" },
|
||||||
placeholder: cx.props.placeholder,
|
placeholder: props.placeholder,
|
||||||
value: cx.props.value,
|
value: props.value,
|
||||||
|
|
||||||
oninput: move |evt| {
|
oninput: move |evt| {
|
||||||
if let Some(cb) = &cx.props.oninput {
|
if let Some(cb) = &props.oninput {
|
||||||
cb.call(evt);
|
cb.call(evt);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
if let Some(score) = score {
|
if let Some(score) = score {
|
||||||
rsx!(
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::PASSWORD_TEXT_INPUT_STRENGTH_LEVEL,
|
class: ClassName::PASSWORD_TEXT_INPUT_STRENGTH_LEVEL,
|
||||||
Pyramid {
|
Pyramid {
|
||||||
ratio: score,
|
ratio: score,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::PASSWORD_TEXT_INPUT_SHOW_TOGGLE,
|
class: ClassName::PASSWORD_TEXT_INPUT_SHOW_TOGGLE,
|
||||||
onclick: move |_| {
|
onclick: move |_| {
|
||||||
show_password.set(!**show_password);
|
let current_state = *show_password.read();
|
||||||
|
show_password.set(!current_state);
|
||||||
},
|
},
|
||||||
|
|
||||||
if **show_password {
|
if *show_password.read() {
|
||||||
rsx!(
|
|
||||||
Icon {
|
Icon {
|
||||||
icon: IoEyeOff,
|
icon: IoEyeOff,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rsx!(
|
|
||||||
Icon {
|
Icon {
|
||||||
icon: IoEye,
|
icon: IoEye,
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -215,9 +210,9 @@ pub fn PasswordTextInput<'a>(cx: Scope<'a, InputProps<'a, PasswordInputState>>)
|
|||||||
|
|
||||||
p {
|
p {
|
||||||
class: criticity_class,
|
class: criticity_class,
|
||||||
helper_text
|
{helper_text}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,10 +2,9 @@ use dioxus::prelude::*;
|
|||||||
|
|
||||||
turf::style_sheet!("src/components/wallpaper.scss");
|
turf::style_sheet!("src/components/wallpaper.scss");
|
||||||
|
|
||||||
#[component]
|
pub fn Wallpaper() -> Element {
|
||||||
pub fn Wallpaper(cx: Scope) -> Element {
|
rsx! {
|
||||||
cx.render(rsx! {
|
style { {STYLE_SHEET} },
|
||||||
style { STYLE_SHEET },
|
|
||||||
div {
|
div {
|
||||||
class: ClassName::WALLPAPER,
|
class: ClassName::WALLPAPER,
|
||||||
|
|
||||||
@@ -13,5 +12,5 @@ pub fn Wallpaper(cx: Scope) -> Element {
|
|||||||
class: ClassName::WALLPAPER_CONTENT,
|
class: ClassName::WALLPAPER_CONTENT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
127
src/main.rs
127
src/main.rs
@@ -5,121 +5,105 @@ pub mod matrix_interface;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_desktop::Config;
|
|
||||||
use fermi::*;
|
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
use tracing::{debug, Level};
|
use tracing::{debug, Level};
|
||||||
|
|
||||||
use crate::base::{login, sync_rooms, APP_SETTINGS, CHATS_WIN_INTERFACE, ROOMS, SESSION};
|
use crate::base::{login, sync_rooms};
|
||||||
use crate::components::chats_window::{ChatsWindow, ChatsWindowProps};
|
use crate::base::{APP_SETTINGS, ROOMS, SESSION};
|
||||||
use crate::components::loading::LoadingPage;
|
use crate::components::loading::LoadingPage;
|
||||||
use crate::components::login::Login;
|
use crate::components::login::Login;
|
||||||
use crate::components::main_window::MainWindow;
|
use crate::components::main_window::MainWindow;
|
||||||
|
|
||||||
mod base;
|
mod base;
|
||||||
|
|
||||||
fn App(cx: Scope) -> Element {
|
fn app() -> Element {
|
||||||
debug!("*** App rendering ***");
|
debug!("*** App rendering ***");
|
||||||
|
|
||||||
use_init_atom_root(cx);
|
let mut ready = use_signal(|| false);
|
||||||
|
|
||||||
let app_settings_ref = use_atom_ref(cx, &APP_SETTINGS);
|
|
||||||
let session_ref = use_atom_ref(cx, &SESSION);
|
|
||||||
let rooms_ref = use_atom_ref(cx, &ROOMS);
|
|
||||||
let chats_win_interface_ref = use_atom_ref(cx, &CHATS_WIN_INTERFACE);
|
|
||||||
|
|
||||||
let ready = use_state(cx, || false);
|
|
||||||
|
|
||||||
// Dummy timer simulating the loading of the application
|
// Dummy timer simulating the loading of the application
|
||||||
let _: &Coroutine<()> = use_coroutine(cx, |_: UnboundedReceiver<_>| {
|
let _: Coroutine<()> = use_coroutine(|_: UnboundedReceiver<_>| async move {
|
||||||
to_owned![ready];
|
|
||||||
async move {
|
|
||||||
debug!("Not ready");
|
debug!("Not ready");
|
||||||
sleep(Duration::from_secs(3)).await;
|
sleep(Duration::from_secs(3)).await;
|
||||||
// sleep(Duration::from_secs(0)).await;
|
// sleep(Duration::from_secs(0)).await;
|
||||||
debug!("Ready");
|
debug!("Ready");
|
||||||
ready.set(true);
|
ready.set(true);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let chats_win_state = use_state(cx, || None);
|
let login_coro = use_coroutine(|rx| login(rx, &APP_SETTINGS, &SESSION));
|
||||||
|
|
||||||
let login_coro = use_coroutine(cx, |rx| {
|
|
||||||
login(rx, app_settings_ref.clone(), session_ref.clone())
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut sync_rooms_coro = None;
|
let mut sync_rooms_coro = None;
|
||||||
|
|
||||||
if let Some(requester) = &app_settings_ref.read().requester {
|
if let Some(requester) = &APP_SETTINGS.read().requester {
|
||||||
sync_rooms_coro = Some(use_coroutine(cx, |rx| {
|
sync_rooms_coro = Some(use_coroutine(|rx| {
|
||||||
sync_rooms(rx, requester.borrow().receivers.clone(), rooms_ref.clone())
|
sync_rooms(rx, requester.borrow().receivers.clone(), &ROOMS)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !session_ref.read().is_logged {
|
if !SESSION.read().is_logged {
|
||||||
login_coro.send(false);
|
login_coro.send(false);
|
||||||
} else {
|
} else {
|
||||||
if let Some(coro) = sync_rooms_coro {
|
if let Some(coro) = sync_rooms_coro {
|
||||||
coro.send(true);
|
coro.send(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if chats_win_state.is_none() {
|
// if chats_win_state.read().is_none() {
|
||||||
let chats_window = dioxus_desktop::use_window(cx);
|
// let chats_window = dioxus_desktop::use_window(cx);
|
||||||
|
|
||||||
let receivers = app_settings_ref
|
// let receivers = app_settings
|
||||||
.read()
|
// .read()
|
||||||
.requester
|
// .requester
|
||||||
.as_ref()
|
// .as_ref()
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.borrow()
|
// .borrow()
|
||||||
.receivers
|
// .receivers
|
||||||
.clone();
|
// .clone();
|
||||||
|
|
||||||
let chats_props = ChatsWindowProps {
|
// let chats_props = ChatsWindowProps {
|
||||||
receivers,
|
// receivers,
|
||||||
interface: chats_win_interface_ref.clone(),
|
// interface: chats_win_interface_ref.clone(),
|
||||||
};
|
// };
|
||||||
|
|
||||||
let chats_dom = VirtualDom::new_with_props(ChatsWindow, chats_props);
|
// let chats_dom = VirtualDom::new_with_props(ChatsWindow, chats_props);
|
||||||
|
|
||||||
let window_cfg = Config::default().with_custom_head(
|
// let window_cfg = Config::default().with_custom_head(
|
||||||
r#"
|
// r#"
|
||||||
<style type="text/css">
|
// <style type="text/css">
|
||||||
html, body {
|
// html, body {
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
|
|
||||||
margin: 0;
|
// margin: 0;
|
||||||
}
|
// }
|
||||||
#main, #bodywrap {
|
// #main, #bodywrap {
|
||||||
height: 100%;
|
// height: 100%;
|
||||||
width: 100%;
|
// width: 100%;
|
||||||
}
|
// }
|
||||||
</style>
|
// </style>
|
||||||
"#
|
// "#
|
||||||
.to_owned(),
|
// .to_owned(),
|
||||||
);
|
// );
|
||||||
|
|
||||||
let chats_window_desktop_service = chats_window.new_window(chats_dom, window_cfg);
|
// let chats_window_desktop_service = chats_window.new_window(chats_dom, window_cfg);
|
||||||
chats_win_state.set(Some(chats_window_desktop_service));
|
// chats_win_state.set(Some(chats_window_desktop_service));
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if **ready {
|
if *ready.read() {
|
||||||
if session_ref.read().is_logged {
|
if SESSION.read().is_logged {
|
||||||
debug!("Should render the MainWindow component");
|
debug!("Should render the MainWindow component");
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
MainWindow {},
|
MainWindow {},
|
||||||
})
|
|
||||||
} else {
|
|
||||||
cx.render(rsx! {
|
|
||||||
Login {},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cx.render(rsx! {
|
rsx! {
|
||||||
|
Login {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rsx! {
|
||||||
LoadingPage {},
|
LoadingPage {},
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +113,5 @@ fn main() {
|
|||||||
.with_max_level(Level::DEBUG)
|
.with_max_level(Level::DEBUG)
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
dioxus_desktop::launch(App);
|
launch(app);
|
||||||
// dioxus_web::launch(App);
|
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::Sender;
|
||||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
|
||||||
use matrix_sdk::Client as MatrixClient;
|
use matrix_sdk::Client as MatrixClient;
|
||||||
use tokio::sync::broadcast::Receiver;
|
use tokio::sync::broadcast::Receiver;
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
@@ -12,7 +12,6 @@ use crate::utils::oneshot;
|
|||||||
pub struct Receivers {
|
pub struct Receivers {
|
||||||
pub room_receiver: RefCell<Receiver<RoomEvent>>,
|
pub room_receiver: RefCell<Receiver<RoomEvent>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Receivers {
|
impl Clone for Receivers {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -20,6 +19,13 @@ impl Clone for Receivers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl PartialEq for Receivers {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.room_receiver
|
||||||
|
.borrow()
|
||||||
|
.same_channel(&other.room_receiver.borrow())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Requester {
|
pub struct Requester {
|
||||||
pub matrix_client: Arc<MatrixClient>,
|
pub matrix_client: Arc<MatrixClient>,
|
||||||
|
Reference in New Issue
Block a user