From 732d4627754b5d8ccad6b26a065fbef27761ba5e Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 31 May 2025 13:38:54 +0200 Subject: [PATCH] Add user id based emoji hash Helpful in scenarios where you want to disambiguate people based on their user id at a glance. --- CHANGELOG.md | 4 ++++ cove-config/src/keys.rs | 4 ++++ cove/src/euph/small_message.rs | 15 ++++++++++++++- cove/src/euph/util.rs | 15 ++++++++++++++- cove/src/store.rs | 4 ++++ cove/src/ui/chat.rs | 8 ++++++++ cove/src/ui/chat/tree.rs | 5 +++++ cove/src/ui/chat/tree/renderer.rs | 2 ++ cove/src/ui/chat/tree/scroll.rs | 1 + cove/src/ui/chat/tree/widgets.rs | 9 ++++++++- cove/src/vault/euph.rs | 18 ++++++++++-------- 11 files changed, 74 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3682c75..e92f49b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ Procedure when bumping the version number: ## Unreleased +### Added + +- Key bindings for emoji-based user id hashing + ### Fixed - `keys.rooms.action.connect_autojoin` connecting to non-autojoin rooms diff --git a/cove-config/src/keys.rs b/cove-config/src/keys.rs index 8b5adeb..47c171c 100644 --- a/cove-config/src/keys.rs +++ b/cove-config/src/keys.rs @@ -104,6 +104,7 @@ default_bindings! { pub fn mark_older_seen => ["ctrl+s"]; pub fn info => ["i"]; pub fn links => ["I"]; + pub fn toggle_nick_emoji => ["e"]; pub fn increase_caesar => ["c"]; pub fn decrease_caesar => ["C"]; } @@ -356,6 +357,9 @@ pub struct TreeAction { /// List links found in message. #[serde(default = "default::tree_action::links")] pub links: KeyBinding, + /// Toggle agent id based nick emoji. + #[serde(default = "default::tree_action::toggle_nick_emoji")] + pub toggle_nick_emoji: KeyBinding, /// Increase caesar cipher rotation. #[serde(default = "default::tree_action::increase_caesar")] pub increase_caesar: KeyBinding, diff --git a/cove/src/euph/small_message.rs b/cove/src/euph/small_message.rs index 003cd40..d306226 100644 --- a/cove/src/euph/small_message.rs +++ b/cove/src/euph/small_message.rs @@ -1,15 +1,20 @@ +use std::hash::{DefaultHasher, Hash, Hasher}; + use crossterm::style::Stylize; -use euphoxide::api::{MessageId, Snowflake, Time}; +use euphoxide::api::{MessageId, Snowflake, Time, UserId}; use jiff::Timestamp; use toss::{Style, Styled}; use crate::{store::Msg, ui::ChatMsg}; +use super::util; + #[derive(Debug, Clone)] pub struct SmallMessage { pub id: MessageId, pub parent: Option, pub time: Time, + pub user_id: UserId, pub nick: String, pub content: String, pub seen: bool, @@ -70,6 +75,14 @@ impl Msg for SmallMessage { fn last_possible_id() -> Self::Id { MessageId(Snowflake::MAX) } + + fn nick_emoji(&self) -> Option { + let mut hasher = DefaultHasher::new(); + self.user_id.0.hash(&mut hasher); + let hash = hasher.finish(); + let emoji = &util::EMOJI_LIST[hash as usize % util::EMOJI_LIST.len()]; + Some(emoji.clone()) + } } impl ChatMsg for SmallMessage { diff --git a/cove/src/euph/util.rs b/cove/src/euph/util.rs index c2928ab..ecea304 100644 --- a/cove/src/euph/util.rs +++ b/cove/src/euph/util.rs @@ -1,4 +1,4 @@ -use std::sync::LazyLock; +use std::{collections::HashSet, sync::LazyLock}; use crossterm::style::{Color, Stylize}; use euphoxide::Emoji; @@ -6,6 +6,19 @@ use toss::{Style, Styled}; pub static EMOJI: LazyLock = LazyLock::new(Emoji::load); +pub static EMOJI_LIST: LazyLock> = LazyLock::new(|| { + let mut list = EMOJI + .0 + .values() + .flatten() + .cloned() + .collect::>() + .into_iter() + .collect::>(); + list.sort_unstable(); + list +}); + /// Convert HSL to RGB following [this approach from wikipedia][1]. /// /// `h` must be in the range `[0, 360]`, `s` and `l` in the range `[0, 1]`. diff --git a/cove/src/store.rs b/cove/src/store.rs index f64f71e..b7031c1 100644 --- a/cove/src/store.rs +++ b/cove/src/store.rs @@ -8,6 +8,10 @@ pub trait Msg { fn parent(&self) -> Option; fn seen(&self) -> bool; + fn nick_emoji(&self) -> Option { + None + } + fn last_possible_id() -> Self::Id; } diff --git a/cove/src/ui/chat.rs b/cove/src/ui/chat.rs index 405339b..02adeb7 100644 --- a/cove/src/ui/chat.rs +++ b/cove/src/ui/chat.rs @@ -37,6 +37,7 @@ pub struct ChatState> { cursor: Cursor, editor: EditorState, + nick_emoji: bool, caesar: i8, mode: Mode, @@ -48,6 +49,7 @@ impl + Clone> ChatState { Self { cursor: Cursor::Bottom, editor: EditorState::new(), + nick_emoji: false, caesar: 0, mode: Mode::Tree, @@ -79,6 +81,7 @@ impl> ChatState { &mut self.editor, nick, focused, + self.nick_emoji, self.caesar, ) .boxed_async(), @@ -117,6 +120,11 @@ impl> ChatState { Reaction::Composed { parent, content } } + Reaction::NotHandled if event.matches(&keys.tree.action.toggle_nick_emoji) => { + self.nick_emoji = !self.nick_emoji; + Reaction::Handled + } + Reaction::NotHandled if event.matches(&keys.tree.action.increase_caesar) => { self.caesar = (self.caesar + 1).rem_euclid(26); Reaction::Handled diff --git a/cove/src/ui/chat/tree.rs b/cove/src/ui/chat/tree.rs index 043e109..d9905fc 100644 --- a/cove/src/ui/chat/tree.rs +++ b/cove/src/ui/chat/tree.rs @@ -389,6 +389,7 @@ impl> TreeViewState { editor: &'a mut EditorState, nick: String, focused: bool, + nick_emoji: bool, caesar: i8, ) -> TreeView<'a, M, S> { TreeView { @@ -397,6 +398,7 @@ impl> TreeViewState { editor, nick, focused, + nick_emoji, caesar, } } @@ -410,6 +412,8 @@ pub struct TreeView<'a, M: Msg, S: MsgStore> { nick: String, focused: bool, + + nick_emoji: bool, caesar: i8, } @@ -438,6 +442,7 @@ where size, nick: self.nick.clone(), focused: self.focused, + nick_emoji: self.nick_emoji, caesar: self.caesar, last_cursor: self.state.last_cursor.clone(), last_cursor_top: self.state.last_cursor_top, diff --git a/cove/src/ui/chat/tree/renderer.rs b/cove/src/ui/chat/tree/renderer.rs index 142624e..225191b 100644 --- a/cove/src/ui/chat/tree/renderer.rs +++ b/cove/src/ui/chat/tree/renderer.rs @@ -80,6 +80,7 @@ pub struct TreeContext { pub size: Size, pub nick: String, pub focused: bool, + pub nick_emoji: bool, pub caesar: i8, pub last_cursor: Cursor, pub last_cursor_top: i32, @@ -207,6 +208,7 @@ where self.tz.clone(), indent, msg, + self.context.nick_emoji, self.context.caesar, folded_info, ); diff --git a/cove/src/ui/chat/tree/scroll.rs b/cove/src/ui/chat/tree/scroll.rs index ab3ddae..a8a1305 100644 --- a/cove/src/ui/chat/tree/scroll.rs +++ b/cove/src/ui/chat/tree/scroll.rs @@ -22,6 +22,7 @@ where size: self.last_size, nick: self.last_nick.clone(), focused: true, + nick_emoji: false, caesar: 0, last_cursor: self.last_cursor.clone(), last_cursor_top: self.last_cursor_top, diff --git a/cove/src/ui/chat/tree/widgets.rs b/cove/src/ui/chat/tree/widgets.rs index d46920e..dd7fa89 100644 --- a/cove/src/ui/chat/tree/widgets.rs +++ b/cove/src/ui/chat/tree/widgets.rs @@ -59,10 +59,17 @@ pub fn msg( tz: TimeZone, indent: usize, msg: &M, + nick_emoji: bool, caesar: i8, folded_info: Option, ) -> Boxed<'static, Infallible> { - let (nick, mut content) = msg.styled(); + let (mut nick, mut content) = msg.styled(); + + if nick_emoji { + if let Some(emoji) = msg.nick_emoji() { + nick = nick.then_plain("(").then_plain(emoji).then_plain(")"); + } + } if caesar != 0 { // Apply caesar in inverse because we're decoding diff --git a/cove/src/vault/euph.rs b/cove/src/vault/euph.rs index 931091c..4a4109e 100644 --- a/cove/src/vault/euph.rs +++ b/cove/src/vault/euph.rs @@ -611,7 +611,7 @@ impl Action for GetMsg { let msg = conn .query_row( " - SELECT id, parent, time, name, content, seen + SELECT id, parent, time, user_id, name, content, seen FROM euph_msgs WHERE domain = ? AND room = ? @@ -623,9 +623,10 @@ impl Action for GetMsg { id: MessageId(row.get::<_, WSnowflake>(0)?.0), parent: row.get::<_, Option>(1)?.map(|s| MessageId(s.0)), time: row.get::<_, WTime>(2)?.0, - nick: row.get(3)?, - content: row.get(4)?, - seen: row.get(5)?, + user_id: UserId(row.get(3)?), + nick: row.get(4)?, + content: row.get(5)?, + seen: row.get(6)?, }) }, ) @@ -703,7 +704,7 @@ impl Action for GetTree { AND tree.room = euph_msgs.room AND tree.id = euph_msgs.parent ) - SELECT id, parent, time, name, content, seen + SELECT id, parent, time, user_id, name, content, seen FROM euph_msgs JOIN tree USING (domain, room, id) ORDER BY id ASC @@ -716,9 +717,10 @@ impl Action for GetTree { id: MessageId(row.get::<_, WSnowflake>(0)?.0), parent: row.get::<_, Option>(1)?.map(|s| MessageId(s.0)), time: row.get::<_, WTime>(2)?.0, - nick: row.get(3)?, - content: row.get(4)?, - seen: row.get(5)?, + user_id: UserId(row.get(3)?), + nick: row.get(4)?, + content: row.get(5)?, + seen: row.get(6)?, }) }, )?