2 Commits

Author SHA1 Message Date
27934c7fc9 🚨 Fix some clippy warnings 2024-09-08 16:12:33 +02:00
9d95bd4481 Add the capability to join a conversation 2024-09-08 16:07:13 +02:00
17 changed files with 123 additions and 79 deletions

View File

@@ -44,6 +44,7 @@ pub trait RoomMessagingConsumerInterface {
#[async_trait(?Send)]
pub trait RoomMessagingProviderInterface {
async fn get_avatar(&self, id: &RoomId) -> anyhow::Result<Option<Avatar>>;
async fn join(&self, room_id: &RoomId) -> anyhow::Result<bool>;
}
#[async_trait(?Send)]

View File

@@ -314,4 +314,10 @@ impl RoomStoreConsumerInterface for Room {
fn spaces(&self) -> &Vec<SpaceId> {
&self.spaces
}
async fn join(&self) {
if let Some(messaging_provider) = &self.messaging_provider {
let _ = messaging_provider.join(&self.id).await;
}
}
}

View File

@@ -29,11 +29,11 @@ pub trait RoomStoreConsumerInterface {
fn is_direct(&self) -> Option<bool>;
fn name(&self) -> Option<String>;
fn topic(&self) -> Option<String>;
fn spaces(&self) -> &Vec<SpaceId>;
#[allow(dead_code)]
async fn avatar(&self) -> Option<Avatar>;
fn spaces(&self) -> &Vec<SpaceId>;
async fn join(&self);
}
pub trait RoomStoreProviderInterface {

View File

@@ -776,6 +776,19 @@ impl Client {
Ok(None)
}
async fn join_room(&self, room_id: &RoomId) -> anyhow::Result<bool> {
let client = self.client.as_ref().unwrap();
if let Some(room) = client.get_room(room_id) {
return match room.join().await {
Ok(_) => Ok(true),
Err(err) => Err(err.into()),
};
}
Ok(false)
}
async fn work(&mut self, mut rx: UnboundedReceiver<WorkerTask>) {
while let Some(task) = rx.recv().await {
self.run(task).await;
@@ -820,6 +833,9 @@ impl Client {
)
.await;
}
WorkerTask::JoinRoom(id, reply) => {
reply.send(self.join_room(&id).await).await;
}
}
}
}

View File

@@ -362,6 +362,10 @@ impl RoomMessagingProviderInterface for Requester {
async fn get_avatar(&self, room_id: &RoomId) -> anyhow::Result<Option<Avatar>> {
request_to_worker!(self, WorkerTask::GetRoomAvatar, room_id.clone())
}
async fn join(&self, room_id: &RoomId) -> anyhow::Result<bool> {
request_to_worker!(self, WorkerTask::JoinRoom, room_id.clone())
}
}
#[async_trait(?Send)]

View File

@@ -23,6 +23,7 @@ pub enum WorkerTask {
OwnedUserId,
Sender<anyhow::Result<Option<Vec<u8>>>>,
),
JoinRoom(OwnedRoomId, Sender<anyhow::Result<bool>>),
}
impl Debug for WorkerTask {
@@ -61,6 +62,10 @@ impl Debug for WorkerTask {
.field(room_id)
.field(user_id)
.finish(),
WorkerTask::JoinRoom(room_id, _) => f
.debug_tuple("WorkerTask::JoinRoom")
.field(room_id)
.finish(),
}
}
}

View File

@@ -1,8 +1,7 @@
use std::io::Cursor;
use image::imageops::FilterType;
use image::io::Reader;
use image::{DynamicImage, ImageFormat};
use image::{DynamicImage, ImageFormat, ImageReader};
use image::{GenericImage, RgbImage};
use tracing::{error, warn};
@@ -13,7 +12,7 @@ cfg_if! {
}
fn from_raw_to_image(raw: &Vec<u8>) -> Option<DynamicImage> {
match Reader::new(Cursor::new(raw)).with_guessed_format() {
match ImageReader::new(Cursor::new(raw)).with_guessed_format() {
Ok(reader) => match reader.decode() {
Ok(image) => return Some(image),
Err(err) => error!("Unable to decode the image: {}", err),

View File

@@ -99,7 +99,7 @@ fn app() -> Element {
}
} else {
rsx! {
Login {},
Login {}
}
}
}

View File

@@ -43,7 +43,7 @@ macro_rules! svg_text_button {
($name:ident,$style:ident,$icon:ident) => {
pub fn $name(props: ButtonProps) -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
Button {
id: props.id,
@@ -79,7 +79,7 @@ pub struct ButtonProps {
pub fn Button(props: ButtonProps) -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
button {
id: props.id,
@@ -96,8 +96,8 @@ pub fn Button(props: ButtonProps) -> Element {
}
},
{props.children},
},
{props.children}
}
}
}

View File

@@ -2,6 +2,7 @@ use std::{rc::Rc, time::Duration};
use base64::{engine::general_purpose, Engine as _};
use dioxus::prelude::*;
use futures_util::StreamExt;
use tracing::{debug, warn};
use super::{button::Button, icons::SearchIcon, text_input::TextInput};
@@ -431,11 +432,16 @@ pub fn Search() -> Element {
}
}
#[derive(PartialEq)]
enum ConversationOptionsMenuActions {
Join(RoomId),
Close,
}
#[component]
fn ConversationOptionsMenu(
room_id: RoomId,
on_close: EventHandler,
on_join: EventHandler,
callbacks: Coroutine<ConversationOptionsMenuActions>,
) -> Element {
let room = STORE.read().rooms().get(&room_id).unwrap().signal();
@@ -450,7 +456,7 @@ fn ConversationOptionsMenu(
div {
class: ClassName::CONVERSATION_OPTIONS_MENU_INNER_AVATAR,
ConversationAvatar { room_id }
ConversationAvatar { room_id: room_id.clone() }
}
div {
@@ -477,7 +483,9 @@ fn ConversationOptionsMenu(
div {
class: ClassName::CONVERSATION_OPTIONS_MENU_INNER_CLOSE_BUTTON,
RejectButton {
onclick: move |_| on_close(())
onclick: move |_| {
callbacks.send(ConversationOptionsMenuActions::Close);
}
}
}
@@ -485,8 +493,8 @@ fn ConversationOptionsMenu(
class: ClassName::CONVERSATION_OPTIONS_MENU_INNER_JOIN_BUTTON,
JoinButton {
onclick: move |_| {
on_join(());
on_close(());
callbacks.send(ConversationOptionsMenuActions::Join(room_id.clone()));
callbacks.send(ConversationOptionsMenuActions::Close);
}
}
}
@@ -498,26 +506,33 @@ fn ConversationOptionsMenu(
pub fn Conversations() -> Element {
let mut room_id = use_signal(|| None::<RoomId>);
let on_menu_close = move |_| {
room_id.set(None);
};
let on_menu_join = move |_| async move {
let rooms = STORE.read().rooms();
if let Some(room_id) = room_id.read().to_owned() {
if let Some(room) = rooms.get(&room_id) {}
}
};
let on_pressed_conversation = move |id: RoomId| {
room_id.set(Some(id));
};
let callbacks = use_coroutine(
move |mut rx: UnboundedReceiver<ConversationOptionsMenuActions>| async move {
while let Some(action) = rx.next().await {
match action {
ConversationOptionsMenuActions::Join(room_id) => {
let rooms = STORE.read().rooms();
if let Some(room) = rooms.get(&room_id) {
room.join().await;
}
}
ConversationOptionsMenuActions::Close => {
room_id.set(None);
}
}
}
},
);
let menu = match room_id.read().as_ref() {
Some(room_id) => {
let room_id = room_id.clone();
rsx! {
ConversationOptionsMenu { room_id, on_close: on_menu_close, on_join: on_menu_join }
ConversationOptionsMenu { room_id, callbacks }
}
}
None => VNode::empty(),

View File

@@ -15,7 +15,8 @@ macro_rules! transparent_icon {
($name:ident, $icon:ident) => {
pub fn $name() -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
Icon {
class: ClassName::TRANSPARENT_ICON,
icon: $icon,
@@ -52,7 +53,7 @@ impl IconShape for LogoShape {
pub fn LogoIcon() -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
Icon {
icon: LogoShape,
@@ -133,14 +134,14 @@ impl IconShape for PyramidShape {
L {_PYRAMID_EDGES_E1_X} {_PYRAMID_CENTRAL_EDGE_E2_Y} \
M {_PYRAMID_EDGES_E1_X} {_PYRAMID_EDGES_E1_Y} \
V {_PYRAMID_CENTRAL_EDGE_Y_LEN}",
},
}
path {
d: "\
M {_PYRAMID_CENTRAL_EDGE_E2_X} {_PYRAMID_CENTRAL_EDGE_E2_Y} \
V {central_edge_ratio_e2_y} \
L {left_edge_ratio_e1_x} {no_central_edge_ratio_e1_y} \
L {_PYRAMID_LEFT_EDGE_E2_X} {_PYRAMID_LEFT_EDGE_E2_Y} Z",
},
}
path {
d: "\
M {_PYRAMID_CENTRAL_EDGE_E2_X} {_PYRAMID_CENTRAL_EDGE_E2_Y} \
@@ -168,10 +169,11 @@ pub fn Pyramid(props: PyramidProps) -> Element {
.unwrap_or(COLOR_TERNARY_100.to_string());
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
Icon {
class: ClassName::PYRAMID_ICON,
icon: PyramidShape { ratio: props.ratio, color, progress_color },
}
}

View File

@@ -97,27 +97,6 @@ impl Clone for Box<dyn OnValidationError> {
}
}
#[derive(Clone)]
struct TextInputHandler {
state: Signal<TextInputState>,
}
impl TextInputHandler {}
impl OnValidationError for TextInputHandler {
fn reset(&mut self) {
self.state.write().reset();
}
fn invalidate(&mut self, helper_text: String) {
self.state.write().invalidate(helper_text);
}
fn box_clone(&self) -> Box<dyn OnValidationError> {
Box::new(self.clone())
}
}
#[derive(Clone)]
struct UrlInputHandler {
state: Signal<TextInputState>,
@@ -744,7 +723,7 @@ pub fn Login() -> Element {
let confirm_password_classes_str = confirm_password_classes.join(" ");
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
div {
class: "{classes_str}",
@@ -756,73 +735,79 @@ pub fn Login() -> Element {
random_avatar_future.restart()
},
{avatar},
},
{avatar}
}
div {
class: ClassName::LOGIN_HOMESERVER,
TextInput {
placeholder: "Homeserver URL",
value: "{homeserver_url}",
state: homeserver_url_state,
oninput: on_input![data, homeserver_url],
},
},
}
}
div {
class: ClassName::LOGIN_ID,
TextInput {
placeholder: "{id_placeholder}",
value: "{id}",
state: id_state,
oninput: on_input![data, id],
},
},
}
}
div {
class: "{password_classes_str}",
PasswordTextInput {
placeholder: "Password",
value: "{password}",
state: password_state,
oninput: on_input![data, password],
},
},
}
}
div {
class: "{confirm_password_classes_str}",
PasswordTextInput {
placeholder: "Confirm Password",
value: "{confirm_password}",
state: confirm_password_state,
oninput: on_input![data, confirm_password],
}
},
}
div {
class: ClassName::LOGIN_SPINNER,
Spinner {
animate: *spinner_animated.read(),
},
},
}
}
div {
class: ClassName::LOGIN_REGISTER_BUTTON,
RegisterButton {
onclick: on_clicked_register,
},
},
}
}
div {
class: ClassName::LOGIN_LOGIN_BUTTON,
LoginButton {
focus: true,
onclick: on_clicked_login,
},
},
},
}
}
}
{rendered_modal},
{rendered_modal}
}
}

View File

@@ -76,7 +76,7 @@ pub fn Modal(props: ModalProps) -> Element {
Severity::Critical => ErrorButton,
};
icon.as_ref().ok_or(VNode::empty());
let _ = icon.as_ref().ok_or(VNode::empty());
rsx! {
style { {STYLE_SHEET} }

View File

@@ -13,13 +13,14 @@ pub struct SpinnerProps {
pub fn Spinner(props: SpinnerProps) -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
div {
class: ClassName::SPINNER,
Icon {
class: if props.animate { "" } else { ClassName::PAUSED },
icon: LogoShape,
}
}

View File

@@ -10,7 +10,8 @@ pub fn Wallpaper(display_version: Option<bool>) -> Element {
let version = display_version.map(|flag| if flag { Some(GIT_VERSION) } else { None });
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
div {
class: ClassName::WALLPAPER,
@@ -20,7 +21,8 @@ pub fn Wallpaper(display_version: Option<bool>) -> Element {
div {
class: ClassName::WALLPAPER_VERSION,
{version},
{version}
}
}
}

View File

@@ -7,7 +7,7 @@ turf::style_sheet!("src/ui/layouts/login.scss");
pub fn Login() -> Element {
rsx! {
style { {STYLE_SHEET} },
style { {STYLE_SHEET} }
Wallpaper {
display_version: true

View File

@@ -33,7 +33,6 @@ pub struct Store {
pub struct Room {
store: RefCell<Store>,
#[allow(dead_code)]
domain: Rc<dyn RoomStoreConsumerInterface>,
}
@@ -57,6 +56,10 @@ impl Room {
}
}
pub async fn join(&self) {
self.domain.join().await;
}
#[allow(dead_code)]
pub async fn get_avatar(&self) -> Option<Avatar> {
self.domain.avatar().await
@@ -81,6 +84,11 @@ impl RoomStoreProviderInterface for Room {
fn on_new_member(&self, member: RoomMember) {
let mut store = self.store.borrow_mut();
if member.is_account_user() {
store.is_invited.set(false);
}
store.members.write().push(member);
}