diff --git a/CHANGELOG.md b/CHANGELOG.md
index 24859823..a7214243 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ Added:
- Previously sent messages can be accessed per buffer in the text input with up / down arrows
- Themes directory where users can add their own theme files
- Nickname completions in text input with Tab
+- Broadcast nickname changes to relevant channels and queries.
+- Broadcast quit messages to relevant channels and queries.
Changed:
diff --git a/data/src/client.rs b/data/src/client.rs
index 5b75c28f..d4ae3427 100644
--- a/data/src/client.rs
+++ b/data/src/client.rs
@@ -3,7 +3,7 @@ use std::collections::{BTreeMap, HashMap};
use irc::client::Client;
use itertools::Itertools;
-use crate::user::NickRef;
+use crate::user::{Nick, NickRef};
use crate::{message, Message, Server, User};
#[derive(Debug, Clone, Copy)]
@@ -25,6 +25,25 @@ pub enum State {
Ready(Connection),
}
+#[derive(Debug)]
+pub enum Brodcast {
+ Quit {
+ user: User,
+ comment: Option,
+ },
+ Nickname {
+ old_user: User,
+ new_nick: Nick,
+ ourself: bool,
+ },
+}
+
+#[derive(Debug)]
+pub enum Event {
+ Single(Message),
+ Brodcast(Brodcast),
+}
+
#[derive(Debug)]
pub struct Connection {
client: Client,
@@ -60,37 +79,47 @@ impl Connection {
}
}
- fn receive(&mut self, message: message::Encoded) -> Option {
+ fn receive(&mut self, message: message::Encoded) -> Option {
log::trace!("Message received => {:?}", *message);
- self.handle(&message);
-
- Message::received(message, self.nickname())
+ self.handle(message)
}
- fn handle(&mut self, message: &message::Encoded) {
+ fn handle(&mut self, message: message::Encoded) -> Option {
use irc::proto::{Command, Response};
match &message.command {
Command::NICK(nick) => {
- let Some(old_nick) = message.prefix.as_ref().and_then(|prefix| match prefix {
- irc::proto::Prefix::ServerName(_) => None,
- irc::proto::Prefix::Nickname(nick, _, _) => Some(nick),
- }) else {
- return;
- };
-
- if self.resolved_nick.as_ref() == Some(old_nick) {
- self.resolved_nick = Some(nick.clone())
+ let old_user = message.user()?;
+ let ourself = self.nickname() == old_user.nickname();
+
+ if ourself {
+ self.resolved_nick = Some(nick.clone());
}
+
+ return Some(Event::Brodcast(Brodcast::Nickname {
+ old_user,
+ new_nick: Nick::from(nick.as_str()),
+ ourself,
+ }));
}
Command::Response(Response::RPL_WELCOME, args) => {
if let Some(nick) = args.first() {
self.resolved_nick = Some(nick.to_string());
}
}
+ Command::QUIT(comment) => {
+ let user = message.user()?;
+
+ return Some(Event::Brodcast(Brodcast::Quit {
+ user,
+ comment: comment.clone(),
+ }));
+ }
_ => {}
}
+
+ Some(Event::Single(Message::received(message, self.nickname())?))
}
fn sync(&mut self) {
@@ -183,17 +212,15 @@ impl Map {
self.connection(server).map(Connection::nickname)
}
- pub fn receive(&mut self, server: &Server, message: message::Encoded) -> Option {
+ pub fn receive(&mut self, server: &Server, message: message::Encoded) -> Option {
self.connection_mut(server)
.and_then(|connection| connection.receive(message))
}
- pub fn sync(&mut self) {
- self.0.values_mut().for_each(|state| {
- if let State::Ready(connection) = state {
- connection.sync();
- }
- });
+ pub fn sync(&mut self, server: &Server) {
+ if let Some(State::Ready(connection)) = self.0.get_mut(server) {
+ connection.sync();
+ }
}
pub fn send(&mut self, server: &Server, message: message::Encoded) {
@@ -208,6 +235,24 @@ impl Map {
.unwrap_or_default()
}
+ pub fn get_user_channels(&self, server: &Server, nick: NickRef) -> Vec {
+ self.connection(server)
+ .map(|connection| {
+ connection
+ .channels()
+ .iter()
+ .filter(|channel| {
+ connection
+ .users(channel)
+ .iter()
+ .any(|user| user.nickname() == nick)
+ })
+ .cloned()
+ .collect::>()
+ })
+ .unwrap_or_default()
+ }
+
pub fn iter(&self) -> std::collections::btree_map::Iter {
self.0.iter()
}
diff --git a/data/src/history/manager.rs b/data/src/history/manager.rs
index b74ee47f..911889c9 100644
--- a/data/src/history/manager.rs
+++ b/data/src/history/manager.rs
@@ -9,7 +9,7 @@ use crate::history::{self, History};
use crate::message::{self, Limit};
use crate::time::Posix;
use crate::user::{Nick, NickRef};
-use crate::{server, Buffer, Input, Server};
+use crate::{server, Buffer, Input, Server, User};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Resource {
@@ -260,7 +260,7 @@ impl Manager {
}
})
.cloned();
- let queries = map
+ let mut queries = map
.keys()
.filter_map(|kind| {
if let history::Kind::Query(nick) = kind {
@@ -272,11 +272,43 @@ impl Manager {
.cloned();
let messages = match broadcast {
- Broadcast::Disconnected => {
- message::broadcast::disconnected(channels, queries).collect::>()
+ Broadcast::Disconnected => message::broadcast::disconnected(channels, queries),
+ Broadcast::Reconnected => message::broadcast::reconnected(channels, queries),
+ Broadcast::Quit {
+ user,
+ comment,
+ user_channels,
+ } => {
+ let user_query = queries.find(|nick| user.nickname() == *nick);
+
+ message::broadcast::quit(user_channels, user_query, &user, &comment)
}
- Broadcast::Reconnected => {
- message::broadcast::reconnected(channels, queries).collect::>()
+ Broadcast::Nickname {
+ old_nick,
+ new_nick,
+ ourself,
+ user_channels,
+ } => {
+ if ourself {
+ // If ourself, broadcast to all query channels (since we are in all of them)
+ message::broadcast::nickname(
+ user_channels,
+ queries,
+ &old_nick,
+ &new_nick,
+ ourself,
+ )
+ } else {
+ // Otherwise just the query channel of the user w/ nick change
+ let user_query = queries.find(|nick| old_nick == *nick);
+ message::broadcast::nickname(
+ user_channels,
+ user_query,
+ &old_nick,
+ &new_nick,
+ ourself,
+ )
+ }
}
};
@@ -438,8 +470,19 @@ impl Data {
}
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub enum Broadcast {
Disconnected,
Reconnected,
+ Quit {
+ user: User,
+ comment: Option,
+ user_channels: Vec,
+ },
+ Nickname {
+ old_nick: Nick,
+ new_nick: Nick,
+ ourself: bool,
+ user_channels: Vec,
+ },
}
diff --git a/data/src/message.rs b/data/src/message.rs
index 1cdaa43a..adc15ab3 100644
--- a/data/src/message.rs
+++ b/data/src/message.rs
@@ -12,6 +12,24 @@ pub type Channel = String;
#[derive(Debug, Clone)]
pub struct Encoded(proto::Message);
+impl Encoded {
+ pub fn user(&self) -> Option {
+ fn not_empty(s: &str) -> Option<&str> {
+ (!s.is_empty()).then_some(s)
+ }
+
+ let prefix = self.prefix.as_ref()?;
+ match prefix {
+ proto::Prefix::Nickname(nickname, username, hostname) => Some(User::new(
+ Nick::from(nickname.as_str()),
+ not_empty(username),
+ not_empty(hostname),
+ )),
+ _ => None,
+ }
+ }
+}
+
impl std::ops::Deref for Encoded {
type Target = proto::Message;
@@ -122,24 +140,8 @@ impl Message {
}
}
-fn user(message: &Encoded) -> Option {
- fn not_empty(s: &str) -> Option<&str> {
- (!s.is_empty()).then_some(s)
- }
-
- let prefix = message.prefix.as_ref()?;
- match prefix {
- proto::Prefix::Nickname(nickname, username, hostname) => Some(User::new(
- Nick::from(nickname.as_str()),
- not_empty(username),
- not_empty(hostname),
- )),
- _ => None,
- }
-}
-
fn source(message: Encoded, our_nick: NickRef) -> Option