🎨 Reorganize the contacts_window widgets + add first interactions with homeserver
This commit is contained in:
@@ -1,86 +0,0 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_free_icons::icons::io_icons::IoChevronDown;
|
||||
use dioxus_free_icons::Icon;
|
||||
|
||||
turf::style_sheet!("src/components/contacts.scss");
|
||||
|
||||
fn ContactsArrow(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
Icon {
|
||||
icon: IoChevronDown,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn Contacts(cx: Scope) -> Element {
|
||||
let show_contacts = use_state(cx, || false);
|
||||
|
||||
let classes = vec![
|
||||
ClassName::CONTACTS,
|
||||
if **show_contacts {
|
||||
ClassName::ACTIVE
|
||||
} else {
|
||||
""
|
||||
},
|
||||
]
|
||||
.join(" ");
|
||||
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
div {
|
||||
class: "{classes}",
|
||||
|
||||
p {
|
||||
class: ClassName::HEADER,
|
||||
onclick: move |_| show_contacts.set(!show_contacts),
|
||||
|
||||
ContactsArrow {},
|
||||
|
||||
"Online (4)",
|
||||
},
|
||||
|
||||
// TODO: Test overflow
|
||||
ul {
|
||||
li {
|
||||
img {
|
||||
src: "./images/status_online.png",
|
||||
},
|
||||
p {
|
||||
"Contact AAAAAAAA -",
|
||||
},
|
||||
p {
|
||||
style: "color: darkgrey;",
|
||||
"i'm sad all day until i get to talk with friends, online friends that is",
|
||||
},
|
||||
},
|
||||
li {
|
||||
img {
|
||||
src: "./images/status_busy.png",
|
||||
},
|
||||
p {
|
||||
"Contact BBBBBB -",
|
||||
},
|
||||
p {
|
||||
style: "color: darkgrey;",
|
||||
"i'm sad all day until i get to talk with friends, online friends that is",
|
||||
}
|
||||
},
|
||||
li {
|
||||
img {
|
||||
src: "./images/status_away.png",
|
||||
},
|
||||
p {
|
||||
"Contact CCC -",
|
||||
},
|
||||
p {
|
||||
style: "color: darkgrey;",
|
||||
"i'm sad all day until i get to talk with friends, online friends that is",
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
@@ -1,65 +0,0 @@
|
||||
.contacts {
|
||||
height: 72%;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
font-size: 8pt;
|
||||
|
||||
&.active {
|
||||
ul {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 2%;
|
||||
width: 98%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
margin-left: 1%;
|
||||
padding-top: 1%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: 0.4s ease;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
height: 2%;
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
transition: 0.4s ease;
|
||||
}
|
||||
|
||||
.contact {
|
||||
list-style-type: none;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
cursor: pointer
|
||||
}
|
||||
}
|
47
src/components/contacts_window/contacts.rs
Normal file
47
src/components/contacts_window/contacts.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_std::utils::rw::UseRw;
|
||||
|
||||
use crate::base::Room;
|
||||
use crate::base::Store;
|
||||
use crate::components::contacts_window::contacts_section::ContactsSection;
|
||||
|
||||
turf::style_sheet!("src/components/contacts_window/contacts.scss");
|
||||
|
||||
#[inline_props]
|
||||
pub fn Contacts<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
|
||||
println!("Contacts rendering");
|
||||
|
||||
let store = rw_store.read().unwrap();
|
||||
|
||||
let rooms = &store.rooms;
|
||||
|
||||
let rooms_len = rooms.len();
|
||||
let mut groups = Vec::<Room>::with_capacity(rooms_len);
|
||||
let mut directs = Vec::<Room>::with_capacity(rooms_len);
|
||||
|
||||
for arc_room in rooms.values() {
|
||||
let room = arc_room.read().unwrap().to_owned();
|
||||
|
||||
let is_direct = room.is_direct.unwrap();
|
||||
if is_direct {
|
||||
directs.push(room);
|
||||
} else {
|
||||
groups.push(room);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test overflow
|
||||
// TODO: Add offline users ?
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
div {
|
||||
class: ClassName::CONTACTS,
|
||||
|
||||
ContactsSection {name: "Groups", contacts: RefCell::new(groups)},
|
||||
ContactsSection {name: "Available", contacts: RefCell::new(directs)},
|
||||
},
|
||||
})
|
||||
}
|
6
src/components/contacts_window/contacts.scss
Normal file
6
src/components/contacts_window/contacts.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
@import "../../_base.scss"
|
||||
|
||||
.contacts {
|
||||
height: 72%;
|
||||
background-color: white;
|
||||
}
|
87
src/components/contacts_window/contacts_section.rs
Normal file
87
src/components/contacts_window/contacts_section.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_free_icons::icons::io_icons::IoChevronDown;
|
||||
use dioxus_free_icons::Icon;
|
||||
use matrix_sdk::RoomState;
|
||||
|
||||
use crate::base::Room;
|
||||
|
||||
turf::style_sheet!("src/components/contacts_window/contacts_section.scss");
|
||||
|
||||
fn ContactsArrow(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
Icon {
|
||||
icon: IoChevronDown,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
static NO_SUBJECT_REPR: &str = "No subject";
|
||||
|
||||
#[inline_props]
|
||||
pub fn ContactsSection<'a>(cx: Scope, name: &'a str, contacts: RefCell<Vec<Room>>) -> Element {
|
||||
println!("ContactsSection rendering");
|
||||
|
||||
let show = use_state(cx, || false);
|
||||
|
||||
let classes = vec![
|
||||
ClassName::SECTION,
|
||||
if **show { ClassName::ACTIVE } else { "" },
|
||||
]
|
||||
.join(" ");
|
||||
|
||||
let contacts_len = contacts.borrow().len();
|
||||
|
||||
let rendered_contacts = contacts.borrow_mut().clone().into_iter().map(|room| {
|
||||
let room_name = room.name().unwrap();
|
||||
let is_invited = room.matrix_room.state() == RoomState::Invited;
|
||||
let formatted = format!(
|
||||
"{room_name} - {}",
|
||||
if is_invited {
|
||||
format!("Invited - ")
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
);
|
||||
|
||||
let room_topic = room.topic.unwrap_or(NO_SUBJECT_REPR.to_string()).to_owned();
|
||||
|
||||
rsx!(li {
|
||||
img {
|
||||
src: "./images/status_online.png",
|
||||
},
|
||||
p {
|
||||
formatted,
|
||||
},
|
||||
p {
|
||||
style: "color: darkgrey;",
|
||||
room_topic,
|
||||
},
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
div {
|
||||
class: "{classes}",
|
||||
|
||||
p {
|
||||
class: ClassName::HEADER,
|
||||
onclick: move |_| show.set(!show),
|
||||
|
||||
ContactsArrow {},
|
||||
|
||||
format!("{name} ({contacts_len})"),
|
||||
},
|
||||
|
||||
ul {
|
||||
rendered_contacts.into_iter(),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
67
src/components/contacts_window/contacts_section.scss
Normal file
67
src/components/contacts_window/contacts_section.scss
Normal file
@@ -0,0 +1,67 @@
|
||||
@import "../../_base.scss"
|
||||
|
||||
.section {
|
||||
width: 100%;
|
||||
font-size: $font-size;
|
||||
|
||||
&.active {
|
||||
ul {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
svg {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 2%;
|
||||
width: 98%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
margin-left: 1%;
|
||||
padding-top: 1%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
opacity: 1;
|
||||
transition: 0.4s ease;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
img {
|
||||
height: $icon-size;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
transition: 0.4s ease;
|
||||
}
|
||||
|
||||
.contact {
|
||||
list-style-type: none;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
cursor: pointer
|
||||
}
|
||||
}
|
@@ -1,16 +1,14 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_std::utils::rw::UseRw;
|
||||
|
||||
use crate::app_settings::AppSettings;
|
||||
use crate::components::contacts::Contacts;
|
||||
use crate::components::header::Header;
|
||||
use crate::components::user_infos::UserInfos;
|
||||
|
||||
turf::style_sheet!("src/components/contacts_window.scss");
|
||||
|
||||
pub fn ContactWindow(cx: Scope) -> Element {
|
||||
let app_context = use_shared_state::<AppSettings>(cx).unwrap();
|
||||
use crate::base::Store;
|
||||
use crate::components::contacts_window::contacts::Contacts;
|
||||
use crate::components::contacts_window::user_infos::UserInfos;
|
||||
|
||||
turf::style_sheet!("src/components/contacts_window/contacts_window.scss");
|
||||
|
||||
#[inline_props]
|
||||
pub fn ContactsWindow<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
@@ -28,7 +26,7 @@ pub fn ContactWindow(cx: Scope) -> Element {
|
||||
class: ClassName::USER_INFO,
|
||||
},
|
||||
|
||||
UserInfos {},
|
||||
UserInfos {rw_store: rw_store},
|
||||
},
|
||||
|
||||
div {
|
||||
@@ -86,7 +84,7 @@ pub fn ContactWindow(cx: Scope) -> Element {
|
||||
},
|
||||
},
|
||||
|
||||
Contacts {},
|
||||
Contacts {rw_store: rw_store},
|
||||
|
||||
div {
|
||||
class: ClassName::FOOTER,
|
@@ -1,4 +1,4 @@
|
||||
@import "../_base.scss";
|
||||
@import "../../_base.scss";
|
||||
|
||||
.contactsWindow {
|
||||
width: 100%;
|
5
src/components/contacts_window/mod.rs
Normal file
5
src/components/contacts_window/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod contacts_window;
|
||||
|
||||
mod contacts;
|
||||
mod contacts_section;
|
||||
mod user_infos;
|
@@ -1,10 +1,12 @@
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_free_icons::icons::md_navigation_icons::MdArrowDropDown;
|
||||
use dioxus_free_icons::Icon;
|
||||
use dioxus_std::utils::rw::UseRw;
|
||||
|
||||
use crate::base::Store;
|
||||
use crate::components::avatar_selector::AvatarSelector;
|
||||
|
||||
turf::style_sheet!("src/components/user_infos.scss");
|
||||
turf::style_sheet!("src/components/contacts_window/user_infos.scss");
|
||||
|
||||
fn DownArrowIcon(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
@@ -17,7 +19,18 @@ fn DownArrowIcon(cx: Scope) -> Element {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn UserInfos(cx: Scope) -> Element {
|
||||
static MESSAGE_PLACEHOLDER: &str = "<Enter a personal message>";
|
||||
|
||||
#[inline_props]
|
||||
pub fn UserInfos<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
|
||||
println!("UserInfos rendering");
|
||||
|
||||
let store = rw_store.read().unwrap().clone();
|
||||
|
||||
let user_id = store.user_id.unwrap();
|
||||
let user_info = store.user_infos.get(&user_id).unwrap();
|
||||
let user_display_name = user_info.display_name.as_ref().unwrap();
|
||||
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
@@ -36,7 +49,7 @@ pub fn UserInfos(cx: Scope) -> Element {
|
||||
class: ClassName::USER_ID,
|
||||
p {
|
||||
class: ClassName::USER_NAME,
|
||||
"SUPER USER"
|
||||
"{user_display_name}",
|
||||
},
|
||||
p {
|
||||
class: ClassName::USER_STATUS,
|
||||
@@ -48,7 +61,8 @@ pub fn UserInfos(cx: Scope) -> Element {
|
||||
div {
|
||||
class: ClassName::USER_MESSAGE,
|
||||
p {
|
||||
"My message",
|
||||
// TODO: Handle user message
|
||||
MESSAGE_PLACEHOLDER,
|
||||
}
|
||||
DownArrowIcon {},
|
||||
},
|
@@ -1,4 +1,4 @@
|
||||
@import "../_base.scss"
|
||||
@import "../../_base.scss"
|
||||
|
||||
.userInfo {
|
||||
position: relative;
|
@@ -11,19 +11,15 @@ use crate::matrix_client::{LoginStyle, MatrixClient};
|
||||
|
||||
turf::style_sheet!("src/components/login.scss");
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct LoginProps<'a> {
|
||||
pub store: &'a mut UseRw<Store>,
|
||||
}
|
||||
static EMPTY_PLACEHOLDER: &str = "Tmp placeholder";
|
||||
|
||||
pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
#[inline_props]
|
||||
pub fn Login<'a>(cx: Scope, rw_store: &'a UseRw<Store>) -> Element {
|
||||
let app_context = use_shared_state::<AppSettings>(cx).unwrap();
|
||||
let invalid_login = use_state(cx, || false);
|
||||
let login = use_ref(cx, || Login::new());
|
||||
|
||||
let store = cx.props.store.clone();
|
||||
|
||||
let empty_placeholder = String::from("");
|
||||
let arc_store = Arc::new(rw_store.to_owned().clone());
|
||||
|
||||
let password_class = if **invalid_login {
|
||||
ClassName::INVALID_INPUT
|
||||
@@ -33,14 +29,15 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
|
||||
let run_matrix_client = move |_| {
|
||||
cx.spawn({
|
||||
to_owned![app_context, invalid_login, login, store];
|
||||
to_owned![app_context, invalid_login, login, arc_store];
|
||||
|
||||
let homeserver_url = login.read().homeserver_url.clone().unwrap();
|
||||
let username = login.read().email.clone().unwrap();
|
||||
let password = login.read().password.clone().unwrap();
|
||||
let login_ref = login.read();
|
||||
let homeserver_url = login_ref.homeserver_url.clone().unwrap();
|
||||
let username = login_ref.email.clone().unwrap();
|
||||
let password = login_ref.password.clone().unwrap();
|
||||
|
||||
async move {
|
||||
let requester = MatrixClient::spawn(homeserver_url, store).await;
|
||||
let requester = MatrixClient::spawn(homeserver_url, arc_store.clone()).await;
|
||||
requester.init();
|
||||
|
||||
match requester.login(LoginStyle::Password(username, password)) {
|
||||
@@ -58,6 +55,12 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
});
|
||||
};
|
||||
|
||||
let login_ref = login.read();
|
||||
let placeholder = EMPTY_PLACEHOLDER.to_string();
|
||||
let homeserver_url_value = login_ref.homeserver_url.as_ref().unwrap_or(&placeholder);
|
||||
let email_value = login_ref.email.as_ref().unwrap_or(&placeholder);
|
||||
let password_value = login_ref.password.as_ref().unwrap_or(&placeholder);
|
||||
|
||||
cx.render(rsx! {
|
||||
style { STYLE_SHEET },
|
||||
|
||||
@@ -83,7 +86,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
id: "input-homeserver-url",
|
||||
r#type: "text",
|
||||
name: "homeserver URL",
|
||||
value: "{(login.read().homeserver_url.as_ref().unwrap_or(&empty_placeholder))}",
|
||||
value: "{homeserver_url_value}",
|
||||
oninput: move |evt| login.write().homeserver_url = Some(evt.value.clone()),
|
||||
},
|
||||
|
||||
@@ -94,7 +97,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
id: "login-input-email",
|
||||
r#type: "text",
|
||||
name: "email",
|
||||
value: "{login.read().email.as_ref().unwrap_or(&empty_placeholder)}",
|
||||
value: "{email_value}",
|
||||
oninput: move |evt| login.write().email = Some(evt.value.clone()),
|
||||
},
|
||||
p {
|
||||
@@ -105,7 +108,7 @@ pub fn Login<'a>(cx: Scope<'a, LoginProps<'a>>) -> Element<'a> {
|
||||
id: "login-input-password",
|
||||
r#type: "password",
|
||||
name: "Password",
|
||||
value: "{login.read().password.as_ref().unwrap_or(&empty_placeholder)}",
|
||||
value: "{password_value}",
|
||||
oninput: move |evt| {
|
||||
login.write().password = Some(evt.value.clone());
|
||||
invalid_login.set(false);
|
||||
|
4
src/components/mod.rs
Normal file
4
src/components/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod avatar_selector;
|
||||
pub mod contacts_window;
|
||||
pub mod header;
|
||||
pub mod login;
|
Reference in New Issue
Block a user