aboutsummaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/main.rs126
1 files changed, 92 insertions, 34 deletions
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<String> {
lines
}
+fn contains_mention(msg: &Line, name: &Word) -> bool {
+ let is_word_bound = |c: Option<char>| 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<Reply> {
match conn.send_command(cmd).await {
Ok(Ok(reply)) => Ok(reply),
@@ -141,12 +156,13 @@ struct RoomData {
history: Vec<HItem>,
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<Self>, msg: Message) -> io::Result<()> {
+ async fn add_message(self: &Arc<Self>, 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<Self>, 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<Self>, 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<Self>) -> 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<PushMessage>, app: Arc<App>) {