From ee7121b04e1c0fb9ba09d68aa1e4e670cced877f Mon Sep 17 00:00:00 2001 From: Joscha Date: Fri, 5 Jan 2024 23:21:06 +0100 Subject: [PATCH] Implement live caesar cipher --- CHANGELOG.md | 1 + cove-config/src/keys.rs | 8 +++++++ cove/src/ui/chat.rs | 36 +++++++++++++++++++++++++++---- cove/src/ui/chat/tree.rs | 4 ++++ cove/src/ui/chat/tree/renderer.rs | 3 ++- cove/src/ui/chat/tree/scroll.rs | 1 + cove/src/ui/chat/tree/widgets.rs | 14 ++++++++++++ cove/src/util.rs | 19 ++++++++++++++++ 8 files changed, 81 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ac199d..51bbdc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Procedure when bumping the version number: ### Added - Support for setting window title - More information to room list heading +- Key bindings for live caesar cipher de- and encoding ### Removed - Key binding to open present page diff --git a/cove-config/src/keys.rs b/cove-config/src/keys.rs index 422798e..8b5adeb 100644 --- a/cove-config/src/keys.rs +++ b/cove-config/src/keys.rs @@ -104,6 +104,8 @@ default_bindings! { pub fn mark_older_seen => ["ctrl+s"]; pub fn info => ["i"]; pub fn links => ["I"]; + pub fn increase_caesar => ["c"]; + pub fn decrease_caesar => ["C"]; } } @@ -354,6 +356,12 @@ pub struct TreeAction { /// List links found in message. #[serde(default = "default::tree_action::links")] pub links: KeyBinding, + /// Increase caesar cipher rotation. + #[serde(default = "default::tree_action::increase_caesar")] + pub increase_caesar: KeyBinding, + /// Decrease caesar cipher rotation. + #[serde(default = "default::tree_action::decrease_caesar")] + pub decrease_caesar: KeyBinding, } #[derive(Debug, Default, Deserialize, Document)] diff --git a/cove/src/ui/chat.rs b/cove/src/ui/chat.rs index 6a9c9c2..c8a310c 100644 --- a/cove/src/ui/chat.rs +++ b/cove/src/ui/chat.rs @@ -11,6 +11,7 @@ use toss::widgets::{BoxedAsync, EditorState}; use toss::{Styled, WidgetExt}; use crate::store::{Msg, MsgStore}; +use crate::util; use self::cursor::Cursor; use self::tree::TreeViewState; @@ -33,6 +34,7 @@ pub struct ChatState> { cursor: Cursor, editor: EditorState, + caesar: i8, mode: Mode, tree: TreeViewState, @@ -43,6 +45,7 @@ impl + Clone> ChatState { Self { cursor: Cursor::Bottom, editor: EditorState::new(), + caesar: 0, mode: Mode::Tree, tree: TreeViewState::new(store.clone()), @@ -68,7 +71,13 @@ impl> ChatState { match self.mode { Mode::Tree => self .tree - .widget(&mut self.cursor, &mut self.editor, nick, focused) + .widget( + &mut self.cursor, + &mut self.editor, + nick, + focused, + self.caesar, + ) .boxed_async(), } } @@ -85,7 +94,7 @@ impl> ChatState { S: Send + Sync, S::Error: Send, { - match self.mode { + let reaction = match self.mode { Mode::Tree => { self.tree .handle_input_event( @@ -95,9 +104,28 @@ impl> ChatState { &mut self.editor, can_compose, ) - .await + .await? } - } + }; + + Ok(match reaction { + Reaction::Composed { parent, content } if self.caesar != 0 => { + let content = util::caesar(&content, self.caesar); + Reaction::Composed { parent, content } + } + + Reaction::NotHandled if event.matches(&keys.tree.action.increase_caesar) => { + self.caesar = (self.caesar + 1).rem_euclid(26); + Reaction::Handled + } + + Reaction::NotHandled if event.matches(&keys.tree.action.decrease_caesar) => { + self.caesar = (self.caesar - 1).rem_euclid(26); + Reaction::Handled + } + + reaction => reaction, + }) } pub fn cursor(&self) -> Option<&M::Id> { diff --git a/cove/src/ui/chat/tree.rs b/cove/src/ui/chat/tree.rs index 772363f..37972e5 100644 --- a/cove/src/ui/chat/tree.rs +++ b/cove/src/ui/chat/tree.rs @@ -386,6 +386,7 @@ impl> TreeViewState { editor: &'a mut EditorState, nick: String, focused: bool, + caesar: i8, ) -> TreeView<'a, M, S> { TreeView { state: self, @@ -393,6 +394,7 @@ impl> TreeViewState { editor, nick, focused, + caesar, } } } @@ -405,6 +407,7 @@ pub struct TreeView<'a, M: Msg, S: MsgStore> { nick: String, focused: bool, + caesar: i8, } #[async_trait] @@ -432,6 +435,7 @@ where size, nick: self.nick.clone(), focused: self.focused, + 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 945f77c..99f32b5 100644 --- a/cove/src/ui/chat/tree/renderer.rs +++ b/cove/src/ui/chat/tree/renderer.rs @@ -72,6 +72,7 @@ pub struct TreeContext { pub size: Size, pub nick: String, pub focused: bool, + pub caesar: i8, pub last_cursor: Cursor, pub last_cursor_top: i32, } @@ -190,7 +191,7 @@ where }; let highlighted = highlighted && self.context.focused; - let widget = widgets::msg(highlighted, indent, msg, folded_info); + let widget = widgets::msg(highlighted, indent, msg, self.context.caesar, folded_info); let widget = Self::predraw(widget, self.context.size, self.widthdb); Block::new(TreeBlockId::Msg(msg_id), widget, true) } diff --git a/cove/src/ui/chat/tree/scroll.rs b/cove/src/ui/chat/tree/scroll.rs index 482c7ca..822b0b5 100644 --- a/cove/src/ui/chat/tree/scroll.rs +++ b/cove/src/ui/chat/tree/scroll.rs @@ -20,6 +20,7 @@ where size: self.last_size, nick: self.last_nick.clone(), focused: true, + 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 2f1a1ff..9eb6690 100644 --- a/cove/src/ui/chat/tree/widgets.rs +++ b/cove/src/ui/chat/tree/widgets.rs @@ -7,6 +7,7 @@ use toss::{Style, Styled, WidgetExt}; use crate::store::Msg; use crate::ui::chat::widgets::{Indent, Seen, Time}; use crate::ui::ChatMsg; +use crate::util; pub const PLACEHOLDER: &str = "[...]"; @@ -30,6 +31,10 @@ fn style_indent(highlighted: bool) -> Style { } } +fn style_caesar() -> Style { + Style::new().green() +} + fn style_info() -> Style { Style::new().italic().dark_grey() } @@ -46,10 +51,19 @@ pub fn msg( highlighted: bool, indent: usize, msg: &M, + caesar: i8, folded_info: Option, ) -> Boxed<'static, Infallible> { let (nick, mut content) = msg.styled(); + if caesar != 0 { + // Apply caesar in inverse because we're decoding + let rotated = util::caesar(content.text(), -caesar); + content = content + .then_plain("\n") + .then(format!("{rotated} [rot{caesar}]"), style_caesar()); + } + if let Some(amount) = folded_info { content = content .then_plain("\n") diff --git a/cove/src/util.rs b/cove/src/util.rs index 6bcbf3e..4844d68 100644 --- a/cove/src/util.rs +++ b/cove/src/util.rs @@ -48,3 +48,22 @@ pub fn convert_to_time_zone(tz: &TimeZone, time: OffsetDateTime) -> Option String { + let by = by.rem_euclid(26) as u8; + text.chars() + .map(|c| { + if c.is_ascii_lowercase() { + let c = c as u8 - b'a'; + let c = (c + by) % 26; + (c + b'a') as char + } else if c.is_ascii_uppercase() { + let c = c as u8 - b'A'; + let c = (c + by) % 26; + (c + b'A') as char + } else { + c + } + }) + .collect() +}