Add message timestamps and room sorting capability

This commit is contained in:
Werner Kroneman 2023-12-11 14:15:01 +01:00
parent d11daee5df
commit 53ad01ed41
7 changed files with 126 additions and 22 deletions

56
Cargo.lock generated
View File

@ -38,6 +38,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.75"
@ -455,6 +470,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "chatclient"
version = "0.1.0"
dependencies = [
"chrono",
"confy",
"dioxus",
"dioxus-desktop",
@ -475,7 +491,12 @@ version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.48.5",
]
[[package]]
@ -1748,6 +1769,29 @@ dependencies = [
"tokio-native-tls",
]
[[package]]
name = "iana-time-zone"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@ -4301,6 +4345,15 @@ dependencies = [
"windows-tokens",
]
[[package]]
name = "windows-core"
version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-implement"
version = "0.44.0"
@ -4625,8 +4678,9 @@ dependencies = [
[[package]]
name = "xmpp"
version = "0.5.0"
source = "git+https://gitlab.com/xmpp-rs/xmpp-rs.git?rev=2b433d7036209f35ff7daf033a7b7692e58ab6c3#2b433d7036209f35ff7daf033a7b7692e58ab6c3"
source = "git+https://gitlab.com/werner.kroneman/xmpp-rs.git?rev=b75e0047837c33eb0cd9c405c859e46be4f4bc09#b75e0047837c33eb0cd9c405c859e46be4f4bc09"
dependencies = [
"chrono",
"futures",
"log",
"reqwest",

View File

@ -28,9 +28,10 @@ env_logger = "0.10.1"
dioxus="0.4.3"
dioxus-desktop="0.4.3"
futures-util = "0.3.29"
xmpp = { git = "https://gitlab.com/xmpp-rs/xmpp-rs.git", rev="2b433d7036209f35ff7daf033a7b7692e58ab6c3" }
xmpp = { git = "https://gitlab.com/werner.kroneman/xmpp-rs.git", rev="b75e0047837c33eb0cd9c405c859e46be4f4bc09" }
keyring = "2.1.0"
jid = { version = "0.10.0", features = ["serde"] }
confy = "0.5.1"
serde = "1.0.193"
serde_derive = "1.0"
serde_derive = "1.0"
chrono="0.4.31"

View File

@ -17,6 +17,7 @@
use crate::passwords::Password;
use jid::BareJid;
use std::fmt::Debug;
use chrono::{DateTime, FixedOffset};
#[derive(Debug, Clone, PartialEq)]
pub struct Room {
@ -27,6 +28,7 @@ pub struct Room {
pub struct Message {
pub sender: String,
pub body: String,
pub timestamp: DateTime<FixedOffset>
}
#[derive(Debug)]

View File

@ -24,6 +24,7 @@ use crate::{
no_room_open::NoRoomPlaceholder,
room_view::RoomView,
sidebar::SideBar,
room_list::RoomMeta,
},
xmpp_interface,
xmpp_interface::NetworkCommand,
@ -83,7 +84,12 @@ pub fn App(cx: Scope) -> Element {
// Sidebar.
SideBar {
current_user: user.clone(),
rooms: messages.read().keys().cloned().collect(),
rooms: messages.read().iter().map(|(k, v)| {
RoomMeta {
room: k.clone(),
last_update_time: v.last().map(|x| x.timestamp),
}
}).collect(),
on_room_picked: move |x: BareJid| {
current_room.set(Some(x.clone()));
coroutine.send(NetworkCommand::JoinRoom { room: x });

View File

@ -14,20 +14,24 @@
// 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 std::time::Instant;
use chrono::{DateTime, FixedOffset};
use dioxus::core::{Element, Scope};
use dioxus::core_macro::Props;
use dioxus::html::button;
use dioxus::prelude::*;
use jid::BareJid;
/// The props for the room list widget, including:
/// * The list of rooms to show.
/// * The event handler for when the user clicks a room.
/// * The event handler for when the user clicks the "leave" button for a room.
#[derive(Props)]
pub struct RoomListProps<'a> {
rooms: Vec<BareJid>, // TODO: Should this be a reference of some kind?
on_room_picked: EventHandler<'a, BareJid>,
on_room_left: EventHandler<'a, BareJid>,
#[derive(Clone, Eq, PartialEq)]
pub struct RoomMeta {
pub room: BareJid,
pub last_update_time: Option<DateTime<FixedOffset>>
}
#[derive(Clone, Eq, PartialEq, Copy)]
pub enum SortMethod {
Alphabetical,
Recency,
}
/// A widget that shows a list of rooms known to the client.
@ -36,8 +40,41 @@ pub struct RoomListProps<'a> {
/// select a room or to leave it.
///
/// How "selecting" and "leaving" a room is handled is up to the context.
pub fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
#[component]
pub fn RoomList<'a>(cx: Scope<'a>,
rooms: Vec<RoomMeta>, // TODO: Should this be a reference of some kind?
on_room_picked: EventHandler<'a, BareJid>,
on_room_left: EventHandler<'a, BareJid>) -> Element<'a> {
let sort_method = use_state(cx, || SortMethod::Recency);
let sorted_rooms = use_memo(cx, (rooms,sort_method), |(mut rooms, sort_method)| {
match *sort_method.current() {
SortMethod::Alphabetical => rooms.sort_by_key(|room| room.room.to_string()),
SortMethod::Recency => rooms.sort_by_key(|room| room.last_update_time),
}
rooms
});
render! {
button {
onclick: move |_| {
match *sort_method.current() {
SortMethod::Alphabetical => sort_method.set(SortMethod::Recency),
SortMethod::Recency => sort_method.set(SortMethod::Alphabetical),
}
},
"Sort by: ",
match *sort_method.current() {
SortMethod::Alphabetical => "alphabetical",
SortMethod::Recency => "recency",
}
}
// An <ul> with a list of <li> elements, each containing a room name and a button.
ul {
list_style: "none",
@ -51,12 +88,12 @@ pub fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
display: "flex",
flex_direction: "row",
onclick: |_evt| cx.props.on_room_picked.call(room.to_owned()),
onclick: |_evt| cx.props.on_room_picked.call(room.room.clone()),
// The room name inside a <div>.
div {
flex_grow: 1,
"{room}"
"{room.room}"
}
// A button to leave the room.
@ -64,7 +101,7 @@ pub fn RoomList<'a>(cx: Scope<'a, RoomListProps>) -> Element<'a> {
onclick: |evt| {
evt.stop_propagation();
cx.props.on_room_left.call(room.to_owned());
cx.props.on_room_left.call(room.room.clone());
},
"X"
}

View File

@ -15,7 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::widgets::room_join_widget::RoomJoinWidget;
use crate::widgets::room_list::RoomList;
use crate::widgets::room_list::{RoomList, RoomMeta};
use dioxus::core::{Element, Scope};
use dioxus::core_macro::Props;
use dioxus::html::text;
@ -26,7 +26,7 @@ use jid::BareJid;
#[component]
pub fn SideBar<'a>(cx: Scope<'a, SideBarProps>,
current_user: BareJid,
rooms: Vec<BareJid>,
rooms: Vec<RoomMeta>,
on_room_picked: EventHandler<'a, BareJid>,
on_room_left: EventHandler<'a, BareJid>,
on_join_room: EventHandler<'a, BareJid>,

View File

@ -21,6 +21,7 @@ use jid::BareJid;
use log::{error, info};
use std::collections::HashMap;
use std::future::Future;
use std::time::Instant;
use crate::widgets::login_screen::LoginStatus;
use tokio::select;
@ -69,16 +70,17 @@ async fn handle_event(
m.remove(&room_jid);
});
}
xmpp::Event::ChatMessage(_id, sender, body) => {
xmpp::Event::ChatMessage(_id, sender, body, timestamp) => {
println!("Message from {}: {}", &sender, &body.0);
messages.with_mut(move |m| {
m.entry(sender.clone()).or_insert(vec![]).push(Message {
sender: sender.to_string(),
body: body.0,
timestamp,
});
});
}
xmpp::Event::RoomMessage(_id, room_jid, sender_nick, body) => {
xmpp::Event::RoomMessage(_id, room_jid, sender_nick, body, timestamp) => {
println!(
"Message in {} from {}: {}",
&room_jid, &sender_nick, &body.0
@ -87,10 +89,11 @@ async fn handle_event(
m.entry(room_jid.clone()).or_insert(vec![]).push(Message {
sender: sender_nick,
body: body.0,
timestamp,
})
});
}
xmpp::Event::RoomPrivateMessage(_id, room_jid, sender_nick, body) => {
xmpp::Event::RoomPrivateMessage(_id, room_jid, sender_nick, body, timestamp) => {
println!(
"Private message in {} from {}: {}",
&room_jid, &sender_nick, &body.0
@ -99,6 +102,7 @@ async fn handle_event(
m.entry(room_jid.clone()).or_insert(vec![]).push(Message {
sender: sender_nick,
body: body.0,
timestamp,
})
});
}