From b75f78e092414d39a6d33210eab40fe7742c0b37 Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Thu, 24 Sep 2020 22:26:38 +0200 Subject: rust: Proper room and message highlighting --- rust/src/main.rs | 126 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 34 deletions(-) (limited to 'rust') diff --git a/rust/src/main.rs b/rust/src/main.rs index 1f26d11..0d5767f 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -8,7 +8,7 @@ use futures::stream::StreamExt; use once_cell::sync::Lazy; use tokio::{runtime, task}; use tokio::sync::Mutex as AsyncMutex; -use termion::{clear, color, cursor}; +use termion::{clear, color, cursor, style}; use termion::event::{Event, Key, MouseEvent, MouseButton}; use termion::raw::{IntoRawMode, RawTerminal}; use termion::input::{TermRead, MouseTerminal}; @@ -81,6 +81,21 @@ fn wrap_text(s: &str, maxwid: u16) -> Vec { lines } +fn contains_mention(msg: &Line, name: &Word) -> bool { + let is_word_bound = |c: Option| match c { + Some(c) => !c.is_alphanumeric(), + None => true, + }; + + if let Some(index) = msg.find(name.as_str()) { + let index2 = index + name.len(); + is_word_bound(*&msg[..index].chars().next_back()) && + is_word_bound(*&msg[index2..].chars().next_back()) + } else { + false + } +} + async fn send_command(conn: &Connection, cmd: Command) -> io::Result { match conn.send_command(cmd).await { Ok(Ok(reply)) => Ok(reply), @@ -141,12 +156,13 @@ struct RoomData { history: Vec, maxnicklen: usize, editor: Editor, + highlight: bool, } #[derive(Debug)] enum HItem { - Message(Id), - Service(String), + Message(Id, bool), + Service(String, bool), } impl App { @@ -273,7 +289,7 @@ impl App { } for id in ids { - state.append_history(room, HItem::Message(id))?; + state.append_history(room, HItem::Message(id, false))?; } Ok(()) @@ -284,7 +300,7 @@ impl App { state.append_history(room, item) } - async fn add_message(self: &Arc, msg: Message) -> io::Result<()> { + async fn add_message(self: &Arc, msg: Message, highlight: bool) -> io::Result<()> { debug!("add_message {{id={} room={} user={}}}", msg.id, msg.roomname, msg.username); let mut state = self.state.lock().await; @@ -305,7 +321,7 @@ impl App { } } - state.append_history(&roomname, HItem::Message(msgid))?; + state.append_history(&roomname, HItem::Message(msgid, highlight))?; Ok(()) } @@ -317,11 +333,15 @@ impl App { } PushMessage::Join { roomname: room, username } => { self.add_member(&room, username.clone()).await.unwrap(); - self.append_history_item(&room, HItem::Service(format!("Join: <{}>", username))).await.unwrap(); + self.append_history_item(&room, HItem::Service(format!("Join: <{}>", username), false)).await.unwrap(); } PushMessage::Message(msg) => { - bel::bel(); - self.add_message(msg).await.unwrap(); + let mention = contains_mention(&msg.message, &self.username); + if self.decide_push_notify(&msg.roomname, mention).await { + bel::bel(); + self.add_room_highlight(&msg.roomname).await.unwrap(); + } + self.add_message(msg, mention).await.unwrap(); } PushMessage::Online{ sessions: _, username: _ } => {} } @@ -386,17 +406,23 @@ impl App { Err(line) }; + let room = { + let mut state = self.state.lock().await; + let room = state.currentroom.clone(); + state.user_room_activity(&room); + room + }; + match parsed { Err(msg) => { - let msg = msg.to_string(); - let app = self.clone(); - task::spawn(async move { - let room = app.state.lock().await.currentroom.clone(); - if room == *STATUS_ROOM { - app.append_history_item(&STATUS_ROOM, - HItem::Service("Cannot send messages in this magic status room".to_string()) - ).await.unwrap(); - } else { + if room == *STATUS_ROOM { + self.append_history_item(&STATUS_ROOM, + HItem::Service("Cannot send messages in this magic status room".to_string(), true) + ).await.unwrap(); + } else { + let msg = msg.to_string(); + let app = self.clone(); + task::spawn(async move { let line = Line::try_from(msg).unwrap(); if let Ok(Reply::Number(id)) = send_command(&app.conn, Command::Send { roomname: room.clone(), reply_on: None, message: line.clone() @@ -408,10 +434,10 @@ impl App { username: app.username.clone(), timestamp: std::time::SystemTime::now(), message: line, - }).await.unwrap(); + }, false).await.unwrap(); } - } - }); + }); + } } Ok(("quit", _)) | Ok(("exit", _)) => { @@ -419,13 +445,30 @@ impl App { } Ok((cmd, _)) => { - self.append_history_item(&STATUS_ROOM, HItem::Service(format!("Unknown command: '{}'", cmd))).await?; + self.append_history_item(&STATUS_ROOM, HItem::Service(format!("Unknown command: '{}'", cmd), true)).await?; } } Ok(false) } + async fn decide_push_notify(self: &Arc, room: &Word, mention: bool) -> bool { + let state = self.state.lock().await; + + let private_chat = state.rooms.get(room).unwrap().members.len() <= 2; + if private_chat { + true + } else { + mention + } + } + + async fn add_room_highlight(self: &Arc, room: &Word) -> io::Result<()> { + let mut state = self.state.lock().await; + state.add_room_highlight(room); + state.full_redraw() + } + async fn full_redraw(self: &Arc) -> io::Result<()> { self.state.lock().await.full_redraw() } @@ -442,11 +485,13 @@ impl State { for (i, room) in self.roomlist.iter().enumerate() { print!("{}", cursor::Goto(1, u16::try_from(i).unwrap() + 1)); + if self.rooms.get(room).unwrap().highlight { + print!("{}{}", color::Fg(color::Yellow), style::Bold); + } if room == &self.currentroom { - print!("{}{}{}", color::Bg(color::Blue), room, color::Bg(color::Reset)); - } else { - print!("{}", room); + print!("{}", color::Bg(color::Blue)); } + print!("{}{}", room, style::Reset); } if self.currentroom.len() > 0 { @@ -464,14 +509,14 @@ impl State { let wid = self.layout.nlsepx - nicksepx - 2; let mut y = self.termsize.1 - 3; for item in data.history.iter().rev() { - let (prefix, text_to_format) = match item { - HItem::Message(id) => { + let (prefix, text_to_format, highlight) = match item { + HItem::Message(id, highlight) => { let msg = self.msgs.get(&id).unwrap(); - (format!("<{}>", msg.username), &*msg.message) + (format!("<{}>", msg.username), &*msg.message, *highlight) } - HItem::Service(msg) => { - (String::from(" --"), msg.as_str()) + HItem::Service(msg, highlight) => { + (String::from(" --"), msg.as_str(), *highlight) } }; @@ -479,7 +524,10 @@ impl State { let mut done = false; for (i, line) in lines.iter().enumerate().rev() { if i == 0 { - print!("{}{}", cursor::Goto(nicksepx - u16::try_from(prefix.len()).unwrap(), y + 1), prefix); + print!("{}", cursor::Goto(nicksepx - u16::try_from(prefix.len()).unwrap(), y + 1)); + if highlight { print!("{}", color::Bg(color::Cyan)); } + print!("{}", prefix); + if highlight { print!("{}", color::Bg(color::Reset)); } } print!("{}{}", cursor::Goto(nicksepx + 3, y + 1), line); if y == 0 { done = true; break; } @@ -509,11 +557,11 @@ impl State { fn append_history(&mut self, room: &Word, item: HItem) -> io::Result<()> { let mut data = self.rooms.get_mut(room).unwrap(); match &item { - HItem::Message(id) => { + HItem::Message(id, _) => { let msg = self.msgs.get(&id).unwrap(); data.maxnicklen = data.maxnicklen.max(msg.username.len() + 2); } - HItem::Service(_) => { + HItem::Service(_, _) => { data.maxnicklen = data.maxnicklen.max(3); } } @@ -522,9 +570,19 @@ impl State { } fn switch_buffer(&mut self, index: usize) -> io::Result<()> { - self.currentroom = self.roomlist[index].clone(); + let room = self.roomlist[index].clone(); + self.user_room_activity(&room); + self.currentroom = room; self.full_redraw() } + + fn add_room_highlight(&mut self, room: &Word) { + self.rooms.get_mut(room).unwrap().highlight = true; + } + + fn user_room_activity(&mut self, room: &Word) { + self.rooms.get_mut(room).unwrap().highlight = false; + } } async fn pushchan_thread(mut chan: mpsc::Receiver, app: Arc) { -- cgit v1.2.3-70-g09d2