mirror of
https://gitea.mizah.xyz/mizah/dergchat
synced 2024-11-21 13:45:00 -05:00
More refactoring; fixes to non-compiling parts.
This commit is contained in:
parent
b9639ad85c
commit
f8bb6f1fbd
76
src/configuration.rs
Normal file
76
src/configuration.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Dergchat, a free XMPP client.
|
||||||
|
// Copyright (C) 2023 Werner Kroneman
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
use keyring::Entry;
|
||||||
|
use log::error;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use crate::widgets::login_screen::LoginAttempt;
|
||||||
|
|
||||||
|
/// The configuration struct containing all the configuration options.
|
||||||
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub stored_username: String,
|
||||||
|
pub stored_default_nick: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cache the login attempt into the config and keyring.
|
||||||
|
///
|
||||||
|
/// Specifically, we store the username and default nick in the config file, and the password in the keyring.
|
||||||
|
///
|
||||||
|
/// Note: we do NOT store the password in the config file, because the latter is stored in plaintext.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `attempt` - The login attempt to store.
|
||||||
|
/// * `config` - The current app configuration; UseState should prevent race conditions in writing.
|
||||||
|
pub fn store_login_details(attempt: LoginAttempt, config: &mut UseRef<Configuration>) {
|
||||||
|
|
||||||
|
// Extract the fields from the login attempt.
|
||||||
|
let LoginAttempt {
|
||||||
|
username,
|
||||||
|
default_nick,
|
||||||
|
password,
|
||||||
|
} = attempt;
|
||||||
|
|
||||||
|
// Open the config state and store the username and default nick.
|
||||||
|
config.with_mut(|c| {
|
||||||
|
|
||||||
|
// Open the keyring and store the password.
|
||||||
|
let entry = Entry::new("dergchat", &username).expect("Failed to create keyring entry.");
|
||||||
|
entry.set_password(&password).expect("Failed to set password in keyring.");
|
||||||
|
|
||||||
|
// Store the username and default nick in the config.
|
||||||
|
c.stored_username = username;
|
||||||
|
c.stored_default_nick = default_nick;
|
||||||
|
|
||||||
|
if let Err(e) = confy::store("dergchat", None, c) {
|
||||||
|
error!("Failed to store the config file: {}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the configuration from the config file.
|
||||||
|
///
|
||||||
|
/// On failure, log the error and return the default configuration.
|
||||||
|
pub fn load_config() -> Configuration {
|
||||||
|
match confy::load("dergchat", None) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Failed to load config file: {}", e);
|
||||||
|
Configuration::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@
|
|||||||
mod types;
|
mod types;
|
||||||
mod widgets;
|
mod widgets;
|
||||||
mod xmpp_interface;
|
mod xmpp_interface;
|
||||||
|
mod configuration;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
use dioxus_desktop::Config;
|
use dioxus_desktop::Config;
|
||||||
|
@ -18,6 +18,7 @@ use crate::types::LoginCredentials;
|
|||||||
use crate::widgets::login_screen::LoginAttempt;
|
use crate::widgets::login_screen::LoginAttempt;
|
||||||
use crate::widgets::login_screen::{LoginScreen, LoginStatus};
|
use crate::widgets::login_screen::{LoginScreen, LoginStatus};
|
||||||
use crate::widgets::room_view::RoomView;
|
use crate::widgets::room_view::RoomView;
|
||||||
|
use crate::widgets::no_room_open::NoRoomPlaceholder;
|
||||||
use crate::widgets::sidebar::SideBar;
|
use crate::widgets::sidebar::SideBar;
|
||||||
use crate::xmpp_interface::NetworkCommand;
|
use crate::xmpp_interface::NetworkCommand;
|
||||||
use dioxus::core::{Element, Scope};
|
use dioxus::core::{Element, Scope};
|
||||||
@ -31,7 +32,7 @@ use std::collections::HashMap;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use crate::configuration::load_config;
|
||||||
use crate::xmpp_interface;
|
use crate::xmpp_interface;
|
||||||
|
|
||||||
pub mod login_screen;
|
pub mod login_screen;
|
||||||
@ -40,14 +41,10 @@ pub mod room_list;
|
|||||||
pub mod room_view;
|
pub mod room_view;
|
||||||
pub mod send_message;
|
pub mod send_message;
|
||||||
pub mod sidebar;
|
pub mod sidebar;
|
||||||
|
mod no_room_open;
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
|
||||||
struct Configuration {
|
|
||||||
stored_username: String,
|
|
||||||
stored_default_nick: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn App(cx: Scope) -> Element {
|
pub fn App(cx: Scope) -> Element {
|
||||||
|
|
||||||
let messages = use_ref(cx, || HashMap::new());
|
let messages = use_ref(cx, || HashMap::new());
|
||||||
let current_room = use_state(cx, || None::<BareJid>);
|
let current_room = use_state(cx, || None::<BareJid>);
|
||||||
let connection_status = use_state(cx, || LoginStatus::LoggedOut);
|
let connection_status = use_state(cx, || LoginStatus::LoggedOut);
|
||||||
@ -56,13 +53,7 @@ pub fn App(cx: Scope) -> Element {
|
|||||||
xmpp_interface::run_xmpp_toplevel(connection_status.to_owned(), messages.to_owned(), rx)
|
xmpp_interface::run_xmpp_toplevel(connection_status.to_owned(), messages.to_owned(), rx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let config = use_ref(cx, || match confy::load("dergchat", None) {
|
let config = use_ref(cx, load_config);
|
||||||
Ok(x) => x,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Failed to load config file: {}", e);
|
|
||||||
Configuration::default()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let config = config.to_owned();
|
let config = config.to_owned();
|
||||||
@ -157,6 +148,9 @@ pub fn App(cx: Scope) -> Element {
|
|||||||
current_room.set(None);
|
current_room.set(None);
|
||||||
coroutine.send(NetworkCommand::LeaveRoom { room: x });
|
coroutine.send(NetworkCommand::LeaveRoom { room: x });
|
||||||
},
|
},
|
||||||
|
on_join_room: move |x: BareJid| {
|
||||||
|
coroutine.send(NetworkCommand::JoinRoom { room: x });
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// The current room.
|
// The current room.
|
||||||
@ -173,21 +167,11 @@ pub fn App(cx: Scope) -> Element {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No room selected
|
// No room selected
|
||||||
NoRoomPlaceholder {}
|
rsx! {
|
||||||
|
NoRoomPlaceholder {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn NoRoomPlaceholder(cx: Scope) -> Element {
|
|
||||||
render! {
|
|
||||||
div {
|
|
||||||
padding: "5mm",
|
|
||||||
flex_grow: 1,
|
|
||||||
display: "flex",
|
|
||||||
background_color: "#166322",
|
|
||||||
"No room selected. Pick one from the list on the left."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
31
src/widgets/no_room_open.rs
Normal file
31
src/widgets/no_room_open.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Dergchat, a free XMPP client.
|
||||||
|
// Copyright (C) 2023 Werner Kroneman
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use dioxus::core::{Element, Scope};
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
/// A placeholder widget for when no room is open.
|
||||||
|
pub fn NoRoomPlaceholder(cx: Scope) -> Element {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
padding: "5mm",
|
||||||
|
flex_grow: 1,
|
||||||
|
display: "flex",
|
||||||
|
background_color: "#166322",
|
||||||
|
"No room selected. Pick one from the list on the left."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,18 +18,25 @@ use dioxus::core::{Element, Scope};
|
|||||||
use dioxus::core_macro::Props;
|
use dioxus::core_macro::Props;
|
||||||
use dioxus::hooks::use_state;
|
use dioxus::hooks::use_state;
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
use jid::BareJid;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// The props for the room join widget, including:
|
/// The props for the room join widget, including:
|
||||||
/// * The event handler for when the user clicks the join button, passing in a room name. This name is not validated in any way.
|
/// * The event handler for when the user clicks the join button, passing in a room name. This name is not validated in any way.
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct RoomJoinProps<'a> {
|
pub struct RoomJoinProps<'a> {
|
||||||
on_join_room: EventHandler<'a, String>,
|
on_join_room: EventHandler<'a, BareJid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A simple widget that allows the user to join a room by typing
|
/// A simple widget that allows the user to join a room by typing
|
||||||
/// its name into a text field and clicking a button.
|
/// its name into a text field and clicking a button.
|
||||||
|
///
|
||||||
|
/// Also validates the room name, and displays an error message if the room name is invalid.
|
||||||
pub fn RoomJoinWidget<'a>(cx: Scope<'a, RoomJoinProps>) -> Element<'a> {
|
pub fn RoomJoinWidget<'a>(cx: Scope<'a, RoomJoinProps>) -> Element<'a> {
|
||||||
|
|
||||||
|
// Store the current room name and error message in state.
|
||||||
let room = use_state(cx, || "".to_owned());
|
let room = use_state(cx, || "".to_owned());
|
||||||
|
let error = use_state(cx, || "".to_owned());
|
||||||
|
|
||||||
render! {
|
render! {
|
||||||
div {
|
div {
|
||||||
@ -39,11 +46,19 @@ pub fn RoomJoinWidget<'a>(cx: Scope<'a, RoomJoinProps>) -> Element<'a> {
|
|||||||
flex_grow: 1,
|
flex_grow: 1,
|
||||||
display: "block",
|
display: "block",
|
||||||
oninput: |evt| {
|
oninput: |evt| {
|
||||||
room.set(evt.value.clone());
|
room.set(evt.value.to_string());
|
||||||
|
error.set("".to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
onclick: move |_| cx.props.on_join_room.call(room.current().to_string()),
|
onclick: move |_| {
|
||||||
|
// Validate the room name. If it's a valid Jid, try to join it.
|
||||||
|
// Otherwise, display an error message.
|
||||||
|
match BareJid::from_str(room.current().as_str()) {
|
||||||
|
Ok(jid) => {error.set("".to_string()); cx.props.on_join_room.call(jid);},
|
||||||
|
Err(e) => {error.set(e.to_string());},
|
||||||
|
}
|
||||||
|
},
|
||||||
"Join"
|
"Join"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,15 @@ use crate::widgets::room_list::RoomList;
|
|||||||
use crate::widgets::room_join_widget::RoomJoinWidget;
|
use crate::widgets::room_join_widget::RoomJoinWidget;
|
||||||
|
|
||||||
#[derive(Props)]
|
#[derive(Props)]
|
||||||
pub struct SideBarProps {
|
pub struct SideBarProps<'a> {
|
||||||
pub rooms: Vec<BareJid>,
|
pub rooms: Vec<BareJid>,
|
||||||
pub on_room_picked: EventHandler<'static, BareJid>,
|
pub on_room_picked: EventHandler<'a, BareJid>,
|
||||||
pub on_room_left: EventHandler<'static, BareJid>,
|
pub on_room_left: EventHandler<'a, BareJid>,
|
||||||
|
pub on_join_room: EventHandler<'a, BareJid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A widget that combines the RoomList, RoomJoinWidget, and current user info into a sidebar.
|
/// A widget that combines the RoomList, RoomJoinWidget, and current user info into a sidebar.
|
||||||
pub fn SideBar(cx: Scope<SideBarProps>) -> Element {
|
pub fn SideBar<'a>(cx: Scope<'a, SideBarProps>) -> Element<'a> {
|
||||||
render! {
|
render! {
|
||||||
div {
|
div {
|
||||||
padding: "5mm",
|
padding: "5mm",
|
||||||
@ -46,15 +47,13 @@ pub fn SideBar(cx: Scope<SideBarProps>) -> Element {
|
|||||||
// The list of rooms.
|
// The list of rooms.
|
||||||
RoomList {
|
RoomList {
|
||||||
rooms: cx.props.rooms.clone(),
|
rooms: cx.props.rooms.clone(),
|
||||||
on_room_picked: cx.props.on_room_picked.clone(),
|
on_room_picked: |e| cx.props.on_room_picked.call(e),
|
||||||
on_room_left: cx.props.on_room_left.clone(),
|
on_room_left: |e| cx.props.on_room_left.call(e),
|
||||||
}
|
}
|
||||||
|
|
||||||
// The widget to join a room.
|
// The widget to join a room.
|
||||||
RoomJoinWidget {
|
RoomJoinWidget {
|
||||||
on_join_room: |x: String| {
|
on_join_room: |x| cx.props.on_join_room.call(x),
|
||||||
println!("Joining room: {:?}", x);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user