Compare commits
No commits in common. "1eb8b971f5537aea85015ab48182037f0ffada23" and "b0dcb6b1708362a7906a6212413dd0216d20cd39" have entirely different histories.
1eb8b971f5
...
b0dcb6b170
4 changed files with 45 additions and 136 deletions
21
src/cri.rs
21
src/cri.rs
|
@ -115,11 +115,12 @@ impl Cri {
|
||||||
.borrow()
|
.borrow()
|
||||||
.send(IrcCommand::JOIN(channel.clone(), tokens.next().map(String::from), None).into())
|
.send(IrcCommand::JOIN(channel.clone(), tokens.next().map(String::from), None).into())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.message_log.set_active(Some(&channel));
|
self.message_log.set_active(Some(channel));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_query_command(&mut self, mut tokens: std::str::SplitWhitespace<'_>) {
|
fn handle_query_command(&mut self, mut tokens: std::str::SplitWhitespace<'_>) {
|
||||||
self.message_log.set_active(Some(tokens.next().unwrap()));
|
self.message_log
|
||||||
|
.set_active(Some(tokens.next().unwrap().into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_list_command(&self) {
|
fn handle_list_command(&self) {
|
||||||
|
@ -136,7 +137,11 @@ impl Cri {
|
||||||
timestamp: DateTime<Local>,
|
timestamp: DateTime<Local>,
|
||||||
message_id: Option<&str>,
|
message_id: Option<&str>,
|
||||||
) {
|
) {
|
||||||
if !self.message_log.has_channel(chanlist)
|
let already_joined = self.message_log.has_channel(dbg!(chanlist));
|
||||||
|
self.message_log
|
||||||
|
.on_join(chanlist, source_nickname, ×tamp, message_id);
|
||||||
|
|
||||||
|
if !already_joined
|
||||||
&& source_nickname == &self.nickname
|
&& source_nickname == &self.nickname
|
||||||
&& self.capabilities.contains(&"draft/chathistory".into())
|
&& self.capabilities.contains(&"draft/chathistory".into())
|
||||||
{
|
{
|
||||||
|
@ -155,9 +160,6 @@ impl Cri {
|
||||||
.into(),
|
.into(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
|
||||||
self.message_log
|
|
||||||
.on_join(chanlist, source_nickname, ×tamp, message_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,8 +214,6 @@ impl Application for Cri {
|
||||||
let message_id = tags_map.get("msgid").cloned().flatten();
|
let message_id = tags_map.get("msgid").cloned().flatten();
|
||||||
let message_id = message_id.as_deref();
|
let message_id = message_id.as_deref();
|
||||||
|
|
||||||
let batch = tags_map.get("batch").cloned().flatten();
|
|
||||||
|
|
||||||
match &message.command {
|
match &message.command {
|
||||||
IrcCommand::CAP(_, CapSubCommand::ACK, capability, _) => {
|
IrcCommand::CAP(_, CapSubCommand::ACK, capability, _) => {
|
||||||
let capability = capability.as_ref().unwrap();
|
let capability = capability.as_ref().unwrap();
|
||||||
|
@ -245,7 +245,6 @@ impl Application for Cri {
|
||||||
comment.as_deref(),
|
comment.as_deref(),
|
||||||
×tamp,
|
×tamp,
|
||||||
message_id,
|
message_id,
|
||||||
batch,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,8 +300,8 @@ impl Application for Cri {
|
||||||
|
|
||||||
self.input_value.clear();
|
self.input_value.clear();
|
||||||
}
|
}
|
||||||
ui_message::UiMessage::HandleChannelPress(channel_name) => {
|
ui_message::UiMessage::HandleChannelPress(channel) => {
|
||||||
self.message_log.set_active(channel_name.as_deref())
|
self.message_log.set_active(channel)
|
||||||
}
|
}
|
||||||
ui_message::UiMessage::None => (),
|
ui_message::UiMessage::None => (),
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ mod irc_handler;
|
||||||
mod irc_message;
|
mod irc_message;
|
||||||
mod message_log;
|
mod message_log;
|
||||||
mod ui_message;
|
mod ui_message;
|
||||||
mod util;
|
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::Result;
|
||||||
use iced::{Application, Settings};
|
use iced::{Application, Settings};
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
use crate::{
|
use crate::irc_message::{IrcMessage, MessageDetail};
|
||||||
irc_message::{IrcMessage, MessageDetail},
|
|
||||||
util,
|
|
||||||
};
|
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use iced::{
|
use iced::{
|
||||||
|
@ -10,16 +7,16 @@ use iced::{
|
||||||
Background, Color, Length,
|
Background, Color, Length,
|
||||||
};
|
};
|
||||||
use irc::proto::BatchSubCommand;
|
use irc::proto::BatchSubCommand;
|
||||||
|
use regex::Regex;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub channel_name: Option<String>,
|
|
||||||
|
|
||||||
pub messages: Vec<IrcMessage>,
|
pub messages: Vec<IrcMessage>,
|
||||||
pub message_ids: HashSet<String>,
|
pub message_ids: HashSet<String>,
|
||||||
pub names: Vec<String>,
|
pub names: Vec<String>,
|
||||||
pub topic: Option<String>,
|
|
||||||
|
|
||||||
|
pub topic: Option<String>,
|
||||||
pub unread_messages: i32,
|
pub unread_messages: i32,
|
||||||
pub unread_highlights: i32,
|
pub unread_highlights: i32,
|
||||||
pub unread_events: i32,
|
pub unread_events: i32,
|
||||||
|
@ -31,29 +28,6 @@ pub struct Channel {
|
||||||
pub multiline_message_id: Option<String>,
|
pub multiline_message_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
fn new(channel_name: Option<&str>) -> Self {
|
|
||||||
Self {
|
|
||||||
channel_name: channel_name.map(String::from),
|
|
||||||
|
|
||||||
messages: Vec::new(),
|
|
||||||
message_ids: HashSet::new(),
|
|
||||||
names: Vec::new(),
|
|
||||||
topic: None,
|
|
||||||
|
|
||||||
unread_messages: 0,
|
|
||||||
unread_highlights: 0,
|
|
||||||
unread_events: 0,
|
|
||||||
|
|
||||||
is_multiline: false,
|
|
||||||
multiline_privmsgs: None,
|
|
||||||
multiline_timestamp: None,
|
|
||||||
multiline_nickname: None,
|
|
||||||
multiline_message_id: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MessageLog {
|
pub struct MessageLog {
|
||||||
channels: HashMap<Option<String>, Channel>,
|
channels: HashMap<Option<String>, Channel>,
|
||||||
pub active_channel: Option<String>,
|
pub active_channel: Option<String>,
|
||||||
|
@ -65,7 +39,7 @@ pub struct MessageLog {
|
||||||
impl<'a> MessageLog {
|
impl<'a> MessageLog {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut channels = HashMap::new();
|
let mut channels = HashMap::new();
|
||||||
channels.insert(None, Channel::new(None));
|
channels.insert(None, Default::default());
|
||||||
Self {
|
Self {
|
||||||
channels,
|
channels,
|
||||||
active_channel: None,
|
active_channel: None,
|
||||||
|
@ -87,10 +61,8 @@ impl<'a> MessageLog {
|
||||||
self.channels.get(channel)
|
self.channels.get(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut(&mut self, channel_name: Option<&str>) -> &mut Channel {
|
pub fn get_mut(&mut self, channel_name: Option<String>) -> &mut Channel {
|
||||||
self.channels
|
self.channels.entry(channel_name).or_default()
|
||||||
.entry(channel_name.map(String::from))
|
|
||||||
.or_insert_with(|| Channel::new(channel_name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_join(
|
pub fn on_join(
|
||||||
|
@ -101,7 +73,7 @@ impl<'a> MessageLog {
|
||||||
message_id: Option<&str>,
|
message_id: Option<&str>,
|
||||||
) {
|
) {
|
||||||
let is_active = self.active_channel.as_deref() != Some(channel_name);
|
let is_active = self.active_channel.as_deref() != Some(channel_name);
|
||||||
let channel = self.get_mut(Some(channel_name));
|
let channel = self.get_mut(Some(channel_name.into()));
|
||||||
|
|
||||||
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
||||||
return;
|
return;
|
||||||
|
@ -129,7 +101,7 @@ impl<'a> MessageLog {
|
||||||
message_id: Option<&str>,
|
message_id: Option<&str>,
|
||||||
) {
|
) {
|
||||||
let is_active = self.active_channel.as_deref() != Some(channel_name);
|
let is_active = self.active_channel.as_deref() != Some(channel_name);
|
||||||
let channel = self.get_mut(Some(channel_name));
|
let channel = self.get_mut(Some(channel_name.into()));
|
||||||
|
|
||||||
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
||||||
return;
|
return;
|
||||||
|
@ -180,49 +152,22 @@ impl<'a> MessageLog {
|
||||||
reason: Option<&str>,
|
reason: Option<&str>,
|
||||||
timestamp: &DateTime<Local>,
|
timestamp: &DateTime<Local>,
|
||||||
message_id: Option<&str>,
|
message_id: Option<&str>,
|
||||||
batch: Option<String>,
|
|
||||||
) {
|
) {
|
||||||
if let Some(batch) = batch {
|
// TODO increment event counter for each relevant channel
|
||||||
let (subcommand, channel_name) = &self.batch_channels[&batch];
|
for channel in self.channels.values_mut() {
|
||||||
if subcommand == &BatchSubCommand::CUSTOM("CHATHISTORY".into()) {
|
|
||||||
if let Some(channel) = self.channels.get_mut(&Some(channel_name.to_owned())) {
|
|
||||||
if message_id.is_some()
|
|
||||||
&& !channel.message_ids.insert(message_id.unwrap().into())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::add_quit(
|
|
||||||
channel,
|
|
||||||
self.active_channel.as_deref(),
|
|
||||||
nickname,
|
|
||||||
reason,
|
|
||||||
message_id,
|
|
||||||
timestamp,
|
|
||||||
);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (_, channel) in self.channels.iter_mut() {
|
|
||||||
if !channel.names.is_empty() && !channel.names.contains(&nickname.into()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::add_quit(
|
// TODO only show in relevant channels
|
||||||
channel,
|
channel.messages.push(IrcMessage {
|
||||||
self.active_channel.as_deref(),
|
detail: MessageDetail::Quit {
|
||||||
nickname,
|
nickname: nickname.into(),
|
||||||
reason,
|
reason: reason.map(String::from),
|
||||||
message_id,
|
},
|
||||||
timestamp,
|
message_id: message_id.map(String::from),
|
||||||
);
|
timestamp: *timestamp,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +187,8 @@ impl<'a> MessageLog {
|
||||||
(subcommand.unwrap().clone(), channel_name.clone()),
|
(subcommand.unwrap().clone(), channel_name.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let channel = self.get_mut(Some(channel_name));
|
let channel = self.get_mut(Some(channel_name.clone()));
|
||||||
|
|
||||||
channel.is_multiline = true;
|
channel.is_multiline = true;
|
||||||
channel.multiline_privmsgs = Some(Vec::new());
|
channel.multiline_privmsgs = Some(Vec::new());
|
||||||
channel.multiline_timestamp = Some(*timestamp);
|
channel.multiline_timestamp = Some(*timestamp);
|
||||||
|
@ -252,15 +198,10 @@ impl<'a> MessageLog {
|
||||||
tag.into(),
|
tag.into(),
|
||||||
(subcommand.unwrap().clone(), channel_name.clone()),
|
(subcommand.unwrap().clone(), channel_name.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO Collect messages into a separate list and update them
|
|
||||||
// at the end of the batch instead of just clearing the channel
|
|
||||||
let channel = self.get_mut(Some(channel_name));
|
|
||||||
channel.messages.clear();
|
|
||||||
}
|
}
|
||||||
} else if let Some(tag) = tag.strip_prefix('-') {
|
} else if let Some(tag) = tag.strip_prefix('-') {
|
||||||
if let Some((subcommand, channel_name)) = self.batch_channels.remove(tag) {
|
if let Some((subcommand, channel_name)) = self.batch_channels.remove(tag) {
|
||||||
let channel = self.get_mut(Some(&channel_name));
|
let channel = self.get_mut(Some(channel_name.clone()));
|
||||||
|
|
||||||
if subcommand == BatchSubCommand::CUSTOM("DRAFT/MULTILINE".into()) {
|
if subcommand == BatchSubCommand::CUSTOM("DRAFT/MULTILINE".into()) {
|
||||||
channel.is_multiline = false;
|
channel.is_multiline = false;
|
||||||
|
@ -278,7 +219,7 @@ impl<'a> MessageLog {
|
||||||
×tamp,
|
×tamp,
|
||||||
);
|
);
|
||||||
} else if subcommand == BatchSubCommand::CUSTOM("CHATHISTORY".into()) {
|
} else if subcommand == BatchSubCommand::CUSTOM("CHATHISTORY".into()) {
|
||||||
// TODO
|
channel.messages.sort_by_key(|m| m.timestamp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -294,7 +235,7 @@ impl<'a> MessageLog {
|
||||||
timestamp: &DateTime<Local>,
|
timestamp: &DateTime<Local>,
|
||||||
) {
|
) {
|
||||||
let is_active = self.active_channel.as_deref() == Some(channel_name);
|
let is_active = self.active_channel.as_deref() == Some(channel_name);
|
||||||
let channel = self.get_mut(Some(channel_name));
|
let channel = self.get_mut(Some(channel_name.into()));
|
||||||
|
|
||||||
if channel.is_multiline {
|
if channel.is_multiline {
|
||||||
channel.multiline_nickname = Some(nickname.into());
|
channel.multiline_nickname = Some(nickname.into());
|
||||||
|
@ -323,7 +264,9 @@ impl<'a> MessageLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_active {
|
if !is_active {
|
||||||
if util::contains_word(current_nickname, message) {
|
let highlight_regex =
|
||||||
|
Regex::new(&format!(r"\b{}\b", regex::escape(current_nickname))).unwrap();
|
||||||
|
if highlight_regex.is_match(message) {
|
||||||
channel.unread_highlights += 1;
|
channel.unread_highlights += 1;
|
||||||
} else {
|
} else {
|
||||||
channel.unread_messages += 1;
|
channel.unread_messages += 1;
|
||||||
|
@ -341,8 +284,8 @@ impl<'a> MessageLog {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_active(&mut self, channel_name: Option<&str>) {
|
pub fn set_active(&mut self, channel_name: Option<String>) {
|
||||||
self.active_channel = channel_name.map(String::from);
|
self.active_channel = channel_name.clone();
|
||||||
let channel = self.get_mut(channel_name);
|
let channel = self.get_mut(channel_name);
|
||||||
channel.unread_events = 0;
|
channel.unread_events = 0;
|
||||||
channel.unread_messages = 0;
|
channel.unread_messages = 0;
|
||||||
|
@ -446,7 +389,7 @@ impl<'a> MessageLog {
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
container(
|
container(
|
||||||
container(text(format!("{nickname} quit the server{reason}")))
|
container(text(format!("{nickname} left the server{reason}")))
|
||||||
.style(move |_: &_| event_appearance)
|
.style(move |_: &_| event_appearance)
|
||||||
.padding([3, 10]),
|
.padding([3, 10]),
|
||||||
)
|
)
|
||||||
|
@ -519,37 +462,12 @@ impl<'a> MessageLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_topic(&mut self, channel: &str, topic: &str) {
|
pub fn on_topic(&mut self, channel: &str, topic: &str) {
|
||||||
self.get_mut(Some(channel)).topic = Some(topic.into());
|
self.get_mut(Some(channel.into())).topic = Some(topic.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_names_reply(&mut self, channel: &str, names: Vec<&str>) {
|
pub fn on_names_reply(&mut self, channel: &str, names: Vec<&str>) {
|
||||||
self.get_mut(Some(channel)).names.extend(
|
self.get_mut(Some(channel.into()))
|
||||||
names
|
.names
|
||||||
.iter()
|
.extend(names.iter().map(|&n| String::from(n)));
|
||||||
.map(|&n| String::from(n.trim_matches(&['~', '&', '@', '%', '+'] as &[_]))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_quit(
|
|
||||||
channel: &mut Channel,
|
|
||||||
active_channel_name: Option<&str>,
|
|
||||||
nickname: &str,
|
|
||||||
reason: Option<&str>,
|
|
||||||
message_id: Option<&str>,
|
|
||||||
timestamp: &DateTime<Local>,
|
|
||||||
) {
|
|
||||||
let is_active = active_channel_name != Some(channel.channel_name.as_ref().unwrap());
|
|
||||||
if is_active {
|
|
||||||
channel.unread_events += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.messages.push(IrcMessage {
|
|
||||||
detail: MessageDetail::Quit {
|
|
||||||
nickname: nickname.into(),
|
|
||||||
reason: reason.map(String::from),
|
|
||||||
},
|
|
||||||
message_id: message_id.map(String::from),
|
|
||||||
timestamp: *timestamp,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
pub fn contains_word(needle: &str, haystack: &str) -> bool {
|
|
||||||
let pattern = format!(r"\b{}\b", regex::escape(needle));
|
|
||||||
let regex = Regex::new(&pattern).unwrap();
|
|
||||||
regex.is_match(haystack)
|
|
||||||
}
|
|
Loading…
Reference in a new issue