Add user id based emoji hash
Helpful in scenarios where you want to disambiguate people based on their user id at a glance.
This commit is contained in:
parent
40de073799
commit
732d462775
11 changed files with 74 additions and 11 deletions
|
|
@ -15,6 +15,10 @@ Procedure when bumping the version number:
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Key bindings for emoji-based user id hashing
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- `keys.rooms.action.connect_autojoin` connecting to non-autojoin rooms
|
- `keys.rooms.action.connect_autojoin` connecting to non-autojoin rooms
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,7 @@ default_bindings! {
|
||||||
pub fn mark_older_seen => ["ctrl+s"];
|
pub fn mark_older_seen => ["ctrl+s"];
|
||||||
pub fn info => ["i"];
|
pub fn info => ["i"];
|
||||||
pub fn links => ["I"];
|
pub fn links => ["I"];
|
||||||
|
pub fn toggle_nick_emoji => ["e"];
|
||||||
pub fn increase_caesar => ["c"];
|
pub fn increase_caesar => ["c"];
|
||||||
pub fn decrease_caesar => ["C"];
|
pub fn decrease_caesar => ["C"];
|
||||||
}
|
}
|
||||||
|
|
@ -356,6 +357,9 @@ pub struct TreeAction {
|
||||||
/// List links found in message.
|
/// List links found in message.
|
||||||
#[serde(default = "default::tree_action::links")]
|
#[serde(default = "default::tree_action::links")]
|
||||||
pub links: KeyBinding,
|
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.
|
/// Increase caesar cipher rotation.
|
||||||
#[serde(default = "default::tree_action::increase_caesar")]
|
#[serde(default = "default::tree_action::increase_caesar")]
|
||||||
pub increase_caesar: KeyBinding,
|
pub increase_caesar: KeyBinding,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||||
|
|
||||||
use crossterm::style::Stylize;
|
use crossterm::style::Stylize;
|
||||||
use euphoxide::api::{MessageId, Snowflake, Time};
|
use euphoxide::api::{MessageId, Snowflake, Time, UserId};
|
||||||
use jiff::Timestamp;
|
use jiff::Timestamp;
|
||||||
use toss::{Style, Styled};
|
use toss::{Style, Styled};
|
||||||
|
|
||||||
use crate::{store::Msg, ui::ChatMsg};
|
use crate::{store::Msg, ui::ChatMsg};
|
||||||
|
|
||||||
|
use super::util;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SmallMessage {
|
pub struct SmallMessage {
|
||||||
pub id: MessageId,
|
pub id: MessageId,
|
||||||
pub parent: Option<MessageId>,
|
pub parent: Option<MessageId>,
|
||||||
pub time: Time,
|
pub time: Time,
|
||||||
|
pub user_id: UserId,
|
||||||
pub nick: String,
|
pub nick: String,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub seen: bool,
|
pub seen: bool,
|
||||||
|
|
@ -70,6 +75,14 @@ impl Msg for SmallMessage {
|
||||||
fn last_possible_id() -> Self::Id {
|
fn last_possible_id() -> Self::Id {
|
||||||
MessageId(Snowflake::MAX)
|
MessageId(Snowflake::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn nick_emoji(&self) -> Option<String> {
|
||||||
|
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 {
|
impl ChatMsg for SmallMessage {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::sync::LazyLock;
|
use std::{collections::HashSet, sync::LazyLock};
|
||||||
|
|
||||||
use crossterm::style::{Color, Stylize};
|
use crossterm::style::{Color, Stylize};
|
||||||
use euphoxide::Emoji;
|
use euphoxide::Emoji;
|
||||||
|
|
@ -6,6 +6,19 @@ use toss::{Style, Styled};
|
||||||
|
|
||||||
pub static EMOJI: LazyLock<Emoji> = LazyLock::new(Emoji::load);
|
pub static EMOJI: LazyLock<Emoji> = LazyLock::new(Emoji::load);
|
||||||
|
|
||||||
|
pub static EMOJI_LIST: LazyLock<Vec<String>> = LazyLock::new(|| {
|
||||||
|
let mut list = EMOJI
|
||||||
|
.0
|
||||||
|
.values()
|
||||||
|
.flatten()
|
||||||
|
.cloned()
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
list.sort_unstable();
|
||||||
|
list
|
||||||
|
});
|
||||||
|
|
||||||
/// Convert HSL to RGB following [this approach from wikipedia][1].
|
/// 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]`.
|
/// `h` must be in the range `[0, 360]`, `s` and `l` in the range `[0, 1]`.
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ pub trait Msg {
|
||||||
fn parent(&self) -> Option<Self::Id>;
|
fn parent(&self) -> Option<Self::Id>;
|
||||||
fn seen(&self) -> bool;
|
fn seen(&self) -> bool;
|
||||||
|
|
||||||
|
fn nick_emoji(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn last_possible_id() -> Self::Id;
|
fn last_possible_id() -> Self::Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ pub struct ChatState<M: Msg, S: MsgStore<M>> {
|
||||||
|
|
||||||
cursor: Cursor<M::Id>,
|
cursor: Cursor<M::Id>,
|
||||||
editor: EditorState,
|
editor: EditorState,
|
||||||
|
nick_emoji: bool,
|
||||||
caesar: i8,
|
caesar: i8,
|
||||||
|
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
|
@ -48,6 +49,7 @@ impl<M: Msg, S: MsgStore<M> + Clone> ChatState<M, S> {
|
||||||
Self {
|
Self {
|
||||||
cursor: Cursor::Bottom,
|
cursor: Cursor::Bottom,
|
||||||
editor: EditorState::new(),
|
editor: EditorState::new(),
|
||||||
|
nick_emoji: false,
|
||||||
caesar: 0,
|
caesar: 0,
|
||||||
|
|
||||||
mode: Mode::Tree,
|
mode: Mode::Tree,
|
||||||
|
|
@ -79,6 +81,7 @@ impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {
|
||||||
&mut self.editor,
|
&mut self.editor,
|
||||||
nick,
|
nick,
|
||||||
focused,
|
focused,
|
||||||
|
self.nick_emoji,
|
||||||
self.caesar,
|
self.caesar,
|
||||||
)
|
)
|
||||||
.boxed_async(),
|
.boxed_async(),
|
||||||
|
|
@ -117,6 +120,11 @@ impl<M: Msg, S: MsgStore<M>> ChatState<M, S> {
|
||||||
Reaction::Composed { parent, content }
|
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) => {
|
Reaction::NotHandled if event.matches(&keys.tree.action.increase_caesar) => {
|
||||||
self.caesar = (self.caesar + 1).rem_euclid(26);
|
self.caesar = (self.caesar + 1).rem_euclid(26);
|
||||||
Reaction::Handled
|
Reaction::Handled
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,7 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
||||||
editor: &'a mut EditorState,
|
editor: &'a mut EditorState,
|
||||||
nick: String,
|
nick: String,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
nick_emoji: bool,
|
||||||
caesar: i8,
|
caesar: i8,
|
||||||
) -> TreeView<'a, M, S> {
|
) -> TreeView<'a, M, S> {
|
||||||
TreeView {
|
TreeView {
|
||||||
|
|
@ -397,6 +398,7 @@ impl<M: Msg, S: MsgStore<M>> TreeViewState<M, S> {
|
||||||
editor,
|
editor,
|
||||||
nick,
|
nick,
|
||||||
focused,
|
focused,
|
||||||
|
nick_emoji,
|
||||||
caesar,
|
caesar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -410,6 +412,8 @@ pub struct TreeView<'a, M: Msg, S: MsgStore<M>> {
|
||||||
|
|
||||||
nick: String,
|
nick: String,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
|
|
||||||
|
nick_emoji: bool,
|
||||||
caesar: i8,
|
caesar: i8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,6 +442,7 @@ where
|
||||||
size,
|
size,
|
||||||
nick: self.nick.clone(),
|
nick: self.nick.clone(),
|
||||||
focused: self.focused,
|
focused: self.focused,
|
||||||
|
nick_emoji: self.nick_emoji,
|
||||||
caesar: self.caesar,
|
caesar: self.caesar,
|
||||||
last_cursor: self.state.last_cursor.clone(),
|
last_cursor: self.state.last_cursor.clone(),
|
||||||
last_cursor_top: self.state.last_cursor_top,
|
last_cursor_top: self.state.last_cursor_top,
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ pub struct TreeContext<Id> {
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
pub nick: String,
|
pub nick: String,
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
|
pub nick_emoji: bool,
|
||||||
pub caesar: i8,
|
pub caesar: i8,
|
||||||
pub last_cursor: Cursor<Id>,
|
pub last_cursor: Cursor<Id>,
|
||||||
pub last_cursor_top: i32,
|
pub last_cursor_top: i32,
|
||||||
|
|
@ -207,6 +208,7 @@ where
|
||||||
self.tz.clone(),
|
self.tz.clone(),
|
||||||
indent,
|
indent,
|
||||||
msg,
|
msg,
|
||||||
|
self.context.nick_emoji,
|
||||||
self.context.caesar,
|
self.context.caesar,
|
||||||
folded_info,
|
folded_info,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ where
|
||||||
size: self.last_size,
|
size: self.last_size,
|
||||||
nick: self.last_nick.clone(),
|
nick: self.last_nick.clone(),
|
||||||
focused: true,
|
focused: true,
|
||||||
|
nick_emoji: false,
|
||||||
caesar: 0,
|
caesar: 0,
|
||||||
last_cursor: self.last_cursor.clone(),
|
last_cursor: self.last_cursor.clone(),
|
||||||
last_cursor_top: self.last_cursor_top,
|
last_cursor_top: self.last_cursor_top,
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,17 @@ pub fn msg<M: Msg + ChatMsg>(
|
||||||
tz: TimeZone,
|
tz: TimeZone,
|
||||||
indent: usize,
|
indent: usize,
|
||||||
msg: &M,
|
msg: &M,
|
||||||
|
nick_emoji: bool,
|
||||||
caesar: i8,
|
caesar: i8,
|
||||||
folded_info: Option<usize>,
|
folded_info: Option<usize>,
|
||||||
) -> Boxed<'static, Infallible> {
|
) -> 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 {
|
if caesar != 0 {
|
||||||
// Apply caesar in inverse because we're decoding
|
// Apply caesar in inverse because we're decoding
|
||||||
|
|
|
||||||
|
|
@ -611,7 +611,7 @@ impl Action for GetMsg {
|
||||||
let msg = conn
|
let msg = conn
|
||||||
.query_row(
|
.query_row(
|
||||||
"
|
"
|
||||||
SELECT id, parent, time, name, content, seen
|
SELECT id, parent, time, user_id, name, content, seen
|
||||||
FROM euph_msgs
|
FROM euph_msgs
|
||||||
WHERE domain = ?
|
WHERE domain = ?
|
||||||
AND room = ?
|
AND room = ?
|
||||||
|
|
@ -623,9 +623,10 @@ impl Action for GetMsg {
|
||||||
id: MessageId(row.get::<_, WSnowflake>(0)?.0),
|
id: MessageId(row.get::<_, WSnowflake>(0)?.0),
|
||||||
parent: row.get::<_, Option<WSnowflake>>(1)?.map(|s| MessageId(s.0)),
|
parent: row.get::<_, Option<WSnowflake>>(1)?.map(|s| MessageId(s.0)),
|
||||||
time: row.get::<_, WTime>(2)?.0,
|
time: row.get::<_, WTime>(2)?.0,
|
||||||
nick: row.get(3)?,
|
user_id: UserId(row.get(3)?),
|
||||||
content: row.get(4)?,
|
nick: row.get(4)?,
|
||||||
seen: row.get(5)?,
|
content: row.get(5)?,
|
||||||
|
seen: row.get(6)?,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -703,7 +704,7 @@ impl Action for GetTree {
|
||||||
AND tree.room = euph_msgs.room
|
AND tree.room = euph_msgs.room
|
||||||
AND tree.id = euph_msgs.parent
|
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
|
FROM euph_msgs
|
||||||
JOIN tree USING (domain, room, id)
|
JOIN tree USING (domain, room, id)
|
||||||
ORDER BY id ASC
|
ORDER BY id ASC
|
||||||
|
|
@ -716,9 +717,10 @@ impl Action for GetTree {
|
||||||
id: MessageId(row.get::<_, WSnowflake>(0)?.0),
|
id: MessageId(row.get::<_, WSnowflake>(0)?.0),
|
||||||
parent: row.get::<_, Option<WSnowflake>>(1)?.map(|s| MessageId(s.0)),
|
parent: row.get::<_, Option<WSnowflake>>(1)?.map(|s| MessageId(s.0)),
|
||||||
time: row.get::<_, WTime>(2)?.0,
|
time: row.get::<_, WTime>(2)?.0,
|
||||||
nick: row.get(3)?,
|
user_id: UserId(row.get(3)?),
|
||||||
content: row.get(4)?,
|
nick: row.get(4)?,
|
||||||
seen: row.get(5)?,
|
content: row.get(5)?,
|
||||||
|
seen: row.get(6)?,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
)?
|
)?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue