diff --git a/crates/libtiny_common/src/lib.rs b/crates/libtiny_common/src/lib.rs index 3dc50bf8..7cea22c0 100644 --- a/crates/libtiny_common/src/lib.rs +++ b/crates/libtiny_common/src/lib.rs @@ -142,23 +142,48 @@ impl Hash for ChanNameRef { } } +#[derive(Debug)] +pub struct ServerIdGen { + next: ServerId, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ServerId(usize); + +impl ServerIdGen { + pub fn new() -> Self { + Self { next: ServerId(0) } + } + + pub fn next(&mut self) -> ServerId { + let next = self.next; + self.next = ServerId(self.next.0 + 1); + next + } +} + /// Target of a message to be shown in a UI. #[derive(Debug)] pub enum MsgTarget<'a> { /// Show the message in the server tab. - Server { serv: &'a str }, + Server { serv_id: ServerId, serv: &'a str }, /// Show the message in the channel tab. Chan { + serv_id: ServerId, serv: &'a str, chan: &'a ChanNameRef, }, /// Show the message in the privmsg tab. - User { serv: &'a str, nick: &'a str }, + User { + serv_id: ServerId, + serv: &'a str, + nick: &'a str, + }, /// Show the message in all tabs of a server. - AllServTabs { serv: &'a str }, + AllServTabs { serv_id: ServerId, serv: &'a str }, /// Show the message in currently active tab. CurrentTab, @@ -168,19 +193,36 @@ pub enum MsgTarget<'a> { #[derive(Debug, Clone, PartialEq, Eq)] pub enum MsgSource { /// Message sent in a server tab. - Serv { serv: String }, + Serv { serv_id: ServerId, serv: String }, /// Message sent in a channel tab. - Chan { serv: String, chan: ChanName }, + Chan { + serv_id: ServerId, + serv: String, + chan: ChanName, + }, /// Message sent in a privmsg tab. - User { serv: String, nick: String }, + User { + serv_id: ServerId, + serv: String, + nick: String, + }, } impl MsgSource { + pub fn serv_id(&self) -> ServerId { + match self { + MsgSource::Serv { serv_id, .. } + | MsgSource::Chan { serv_id, .. } + | MsgSource::User { serv_id, .. } => *serv_id, + } + } + + // TODO: Most uses of this method should be replaced by `serv_id` pub fn serv_name(&self) -> &str { match self { - MsgSource::Serv { serv } + MsgSource::Serv { serv, .. } | MsgSource::Chan { serv, .. } | MsgSource::User { serv, .. } => serv, } @@ -188,12 +230,28 @@ impl MsgSource { pub fn to_target(&self) -> MsgTarget { match self { - MsgSource::Serv { serv } => MsgTarget::Server { serv }, - MsgSource::Chan { serv, chan } => MsgTarget::Chan { + MsgSource::Serv { serv_id, serv } => MsgTarget::Server { + serv_id: *serv_id, + serv, + }, + MsgSource::Chan { + serv_id, + serv, + chan, + } => MsgTarget::Chan { + serv_id: *serv_id, serv, chan: chan.borrow(), }, - MsgSource::User { serv, nick } => MsgTarget::User { serv, nick }, + MsgSource::User { + serv_id, + serv, + nick, + } => MsgTarget::User { + serv_id: *serv_id, + serv, + nick, + }, } } } diff --git a/crates/libtiny_tui/examples/text.rs b/crates/libtiny_tui/examples/text.rs index 3e2e613a..c5b7e666 100644 --- a/crates/libtiny_tui/examples/text.rs +++ b/crates/libtiny_tui/examples/text.rs @@ -17,11 +17,16 @@ fn main() { let local = tokio::task::LocalSet::new(); - let tab = MsgTarget::Server { serv: "mentions" }; - local.block_on(&runtime, async move { let (tui, rcv_ev) = TUI::run(PathBuf::from("../tiny/config.yml")); + let serv_id = tui.mentions_serv_id(); + + let tab = MsgTarget::Server { + serv_id, + serv: "mentions", + }; + let mut text = String::new(); let mut file = File::open("test/lipsum.txt").unwrap(); file.read_to_string(&mut text).unwrap(); diff --git a/crates/libtiny_tui/src/lib.rs b/crates/libtiny_tui/src/lib.rs index 9f731242..24d990dc 100644 --- a/crates/libtiny_tui/src/lib.rs +++ b/crates/libtiny_tui/src/lib.rs @@ -24,7 +24,7 @@ mod widget; mod tests; use crate::tui::{CmdResult, TUIRet}; -use libtiny_common::{ChanNameRef, Event, MsgSource, MsgTarget, TabStyle}; +use libtiny_common::{ChanNameRef, Event, MsgSource, MsgTarget, ServerId, TabStyle}; use term_input::Input; use std::cell::RefCell; @@ -222,16 +222,20 @@ macro_rules! delegate { impl TUI { delegate!(draw()); delegate!(new_server_tab(serv_name: &str, alias: Option,)); - delegate!(close_server_tab(serv_name: &str,)); - delegate!(new_chan_tab(serv_name: &str, chan: &ChanNameRef,)); - delegate!(close_chan_tab(serv_name: &str, chan: &ChanNameRef,)); - delegate!(close_user_tab(serv_name: &str, nick: &str,)); + delegate!(close_server_tab(serv_id: ServerId,)); + delegate!(new_chan_tab( + serv_id: ServerId, + serv_name: &str, + chan: &ChanNameRef, + )); + delegate!(close_chan_tab(serv_id: ServerId, chan: &ChanNameRef,)); + delegate!(close_user_tab(serv_id: ServerId, nick: &str,)); delegate!(add_client_msg(msg: &str, target: &MsgTarget,)); delegate!(add_msg(msg: &str, ts: Tm, target: &MsgTarget,)); delegate!(add_err_msg(msg: &str, ts: Tm, target: &MsgTarget,)); delegate!(add_client_err_msg(msg: &str, target: &MsgTarget,)); - delegate!(clear_nicks(serv_name: &str,)); - delegate!(set_nick(serv_name: &str, new_nick: &str,)); + delegate!(clear_nicks(serv_id: ServerId, serv_name: &str,)); + delegate!(set_nick(serv_id: ServerId, serv_name: &str, new_nick: &str,)); delegate!(add_privmsg( sender: &str, msg: &str, @@ -251,14 +255,19 @@ impl TUI { delegate!(set_topic( topic: &str, ts: Tm, + serv_id: ServerId, serv_name: &str, chan_name: &ChanNameRef, )); delegate!(set_tab_style(style: TabStyle, target: &MsgTarget,)); - pub fn user_tab_exists(&self, serv_name: &str, nick: &str) -> bool { + pub fn mentions_serv_id(&self) -> ServerId { + self.inner.upgrade().unwrap().borrow().mentions_serv_id() + } + + pub fn user_tab_exists(&self, serv_id: ServerId, nick: &str) -> bool { match self.inner.upgrade() { - Some(tui) => tui.borrow().user_tab_exists(serv_name, nick), + Some(tui) => tui.borrow().user_tab_exists(serv_id, nick), None => false, } } diff --git a/crates/libtiny_tui/src/tests/layout.rs b/crates/libtiny_tui/src/tests/layout.rs index 236bbb2b..d6c3ba27 100644 --- a/crates/libtiny_tui/src/tests/layout.rs +++ b/crates/libtiny_tui/src/tests/layout.rs @@ -11,13 +11,17 @@ fn test_join_part_overflow() { let mut tui = TUI::new_test(21, 4); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); tui.add_nick("123456", Some(ts), &target); tui.add_nick("abcdef", Some(ts), &target); @@ -40,13 +44,17 @@ fn test_alignment_long_string() { tui.set_layout(Layout::Aligned { max_nick_len: 12 }); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); tui.add_privmsg( "osa1", @@ -72,10 +80,14 @@ fn test_alignment_long_string() { #[test] fn test_mnemonic_generation() { let mut tui = TUI::new_test(10, 10); - tui.new_chan_tab("s1", ChanNameRef::new("#ab")); - tui.new_chan_tab("s2", ChanNameRef::new("#ab")); - tui.new_chan_tab("s3", ChanNameRef::new("#ab")); - tui.new_chan_tab("s4", ChanNameRef::new("#ab")); + let (s1, _) = tui.new_server_tab("s1", None); + tui.new_chan_tab(s1, "s1", ChanNameRef::new("#ab")); + let (s2, _) = tui.new_server_tab("s2", None); + tui.new_chan_tab(s2, "s2", ChanNameRef::new("#ab")); + let (s3, _) = tui.new_server_tab("s3", None); + tui.new_chan_tab(s3, "s3", ChanNameRef::new("#ab")); + let (s4, _) = tui.new_server_tab("s4", None); + tui.new_chan_tab(s4, "s4", ChanNameRef::new("#ab")); let tabs = tui.get_tabs(); assert_eq!(tabs.len(), 9); // mentions, 4 servers, 4 channels assert_eq!(tabs[2].switch, Some('a')); diff --git a/crates/libtiny_tui/src/tests/mod.rs b/crates/libtiny_tui/src/tests/mod.rs index b554cf45..2cdb229a 100644 --- a/crates/libtiny_tui/src/tests/mod.rs +++ b/crates/libtiny_tui/src/tests/mod.rs @@ -33,8 +33,8 @@ fn init_screen() { fn close_rightmost_tab() { // After closing right-most tab the tab bar should scroll left. let mut tui = TUI::new_test(20, 4); - tui.new_server_tab("irc.server_1.org", None); - tui.new_server_tab("irc.server_2.org", None); + let (serv1_id, _) = tui.new_server_tab("irc.server_1.org", None); + let (serv2_id, _) = tui.new_server_tab("irc.server_2.org", None); tui.next_tab(); tui.next_tab(); tui.draw(); @@ -49,7 +49,7 @@ fn close_rightmost_tab() { // Should scroll left when the server tab is closed. Left arrow should still be visible as // there are still tabs to the left. - tui.close_server_tab("irc.server_2.org"); + tui.close_server_tab(serv2_id); tui.draw(); #[rustfmt::skip] @@ -61,7 +61,7 @@ fn close_rightmost_tab() { expect_screen(screen, &tui.get_front_buffer(), 20, 4, Location::caller()); // Scroll left again, left arrow should disappear this time. - tui.close_server_tab("irc.server_1.org"); + tui.close_server_tab(serv1_id); tui.draw(); #[rustfmt::skip] @@ -78,13 +78,17 @@ fn small_screen_1() { let mut tui = TUI::new_test(21, 3); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); tui.add_nick("123456", Some(ts), &target); tui.add_nick("abcdef", Some(ts), &target); @@ -127,15 +131,19 @@ fn small_screen_2() { let mut tui = TUI::new_test(21, 4); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); - tui.set_topic("Blah blah blah-", ts.clone(), serv, chan); + tui.set_topic("Blah blah blah-", ts.clone(), serv_id, serv, chan); tui.draw(); @@ -164,9 +172,9 @@ fn ctrl_w() { let mut tui = TUI::new_test(30, 3); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); @@ -233,8 +241,8 @@ fn test_text_field_wrap() { let mut tui = TUI::new_test(40, 8); let server = "chat.freenode.net"; - tui.new_server_tab(server, None); - tui.set_nick(server, "x"); + let (serv_id, _) = tui.new_server_tab(server, None); + tui.set_nick(serv_id, server, "x"); // Switch to server tab tui.next_tab(); diff --git a/crates/libtiny_tui/src/tests/resize.rs b/crates/libtiny_tui/src/tests/resize.rs index 103a7fdf..9cbd50a0 100644 --- a/crates/libtiny_tui/src/tests/resize.rs +++ b/crates/libtiny_tui/src/tests/resize.rs @@ -13,13 +13,17 @@ fn test_resize_recalc_scroll() { let mut tui = TUI::new_test(15, 5); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); tui.add_privmsg( "osa1", @@ -86,13 +90,17 @@ fn test_resize_scroll_stick_to_top() { let mut tui = TUI::new_test(18, 10); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); for i in 0..15 { @@ -148,13 +156,17 @@ fn test_resize_no_scroll_stay_on_bottom() { let mut tui = TUI::new_test(18, 10); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); for i in 0..15 { @@ -252,13 +264,17 @@ fn test_resize_scroll_resize() { let mut tui = TUI::new_test(16, 10); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); for i in 0..15 { @@ -322,13 +338,17 @@ fn test_clear_resize_recalc_scroll() { let mut tui = TUI::new_test(15, 5); let serv = "irc.server_1.org"; let chan = ChanNameRef::new("#chan"); - tui.new_server_tab(serv, None); - tui.set_nick(serv, "osa1"); - tui.new_chan_tab(serv, chan); + let (serv_id, _) = tui.new_server_tab(serv, None); + tui.set_nick(serv_id, serv, "osa1"); + tui.new_chan_tab(serv_id, serv, chan); tui.next_tab(); tui.next_tab(); - let target = MsgTarget::Chan { serv, chan }; + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; let ts = time::at_utc(time::Timespec::new(0, 0)); for _ in 0..6 { tui.add_privmsg("osa1", &"1111 ".repeat(3), ts, &target, false, false); diff --git a/crates/libtiny_tui/src/tui.rs b/crates/libtiny_tui/src/tui.rs index 80c08f22..93d1a913 100644 --- a/crates/libtiny_tui/src/tui.rs +++ b/crates/libtiny_tui/src/tui.rs @@ -22,7 +22,7 @@ use crate::notifier::Notifier; use crate::tab::Tab; use crate::widget::WidgetRet; -use libtiny_common::{ChanNameRef, MsgSource, MsgTarget, TabStyle}; +use libtiny_common::{ChanNameRef, MsgSource, MsgTarget, ServerId, ServerIdGen, TabStyle}; use term_input::{Event, Key}; pub use termbox_simple::{CellBuf, Termbox}; @@ -100,6 +100,10 @@ pub struct TUI { /// Config file path config_path: Option, + + // TODO: Not sure if TUI should be maintaining this + server_id_gen: ServerIdGen, + mentions_serv_id: ServerId, } pub(crate) enum CmdResult { @@ -124,6 +128,10 @@ impl TUI { TUI::new_tb(None, tb) } + pub(crate) fn mentions_serv_id(&self) -> ServerId { + self.mentions_serv_id + } + /// Get termbox front buffer. Useful for testing rendering. pub(crate) fn get_front_buffer(&self) -> CellBuf { self.tb.get_front_buffer() @@ -154,6 +162,9 @@ impl TUI { let width = tb.width() as i32; let height = tb.height() as i32; + let mut server_id_gen = ServerIdGen::new(); + let mentions_serv_id = server_id_gen.next(); + let mut tui = TUI { tb, colors: Colors::default(), @@ -166,14 +177,19 @@ impl TUI { h_scroll: 0, key_map: KeyMap::default(), config_path, + server_id_gen, + mentions_serv_id, }; // Init "mentions" tab. This needs to happen right after creating the TUI to be able to // show any errors in TUI. - tui.new_server_tab("mentions", None); + tui.new_server_tab_with_id(mentions_serv_id, "mentions", None); tui.add_client_msg( "Any mentions to you will be listed here.", - &MsgTarget::Server { serv: "mentions" }, + &MsgTarget::Server { + serv_id: mentions_serv_id, + serv: "mentions", + }, ); tui.reload_config(); @@ -182,14 +198,33 @@ impl TUI { fn ignore(&mut self, src: &MsgSource) { match src { - MsgSource::Serv { serv } => { - self.toggle_ignore(&MsgTarget::AllServTabs { serv }); + MsgSource::Serv { serv_id, serv } => { + self.toggle_ignore(&MsgTarget::AllServTabs { + serv_id: *serv_id, + serv, + }); } - MsgSource::Chan { serv, chan } => { - self.toggle_ignore(&MsgTarget::Chan { serv, chan }); + MsgSource::Chan { + serv_id, + serv, + chan, + } => { + self.toggle_ignore(&MsgTarget::Chan { + serv_id: *serv_id, + serv, + chan, + }); } - MsgSource::User { serv, nick } => { - self.toggle_ignore(&MsgTarget::User { serv, nick }); + MsgSource::User { + serv_id, + serv, + nick, + } => { + self.toggle_ignore(&MsgTarget::User { + serv_id: *serv_id, + serv, + nick, + }); } } } @@ -244,13 +279,29 @@ impl TUI { } }; // can't use `MsgSource::to_target` here, `Serv` case is different - let tab_target = match src { - MsgSource::Serv { ref serv } => MsgTarget::AllServTabs { serv }, - MsgSource::Chan { ref serv, ref chan } => MsgTarget::Chan { + let tab_target = match &src { + MsgSource::Serv { serv_id, serv } => MsgTarget::AllServTabs { + serv_id: *serv_id, + serv, + }, + MsgSource::Chan { + serv_id, + serv, + chan, + } => MsgTarget::Chan { + serv_id: *serv_id, serv, chan: chan.borrow(), }, - MsgSource::User { ref serv, ref nick } => MsgTarget::User { serv, nick }, + MsgSource::User { + serv_id, + serv, + nick, + } => MsgTarget::User { + serv_id: *serv_id, + serv, + nick, + }, }; self.set_notifier(notifier, &tab_target); } @@ -353,7 +404,7 @@ impl TUI { alias: Option, ) { let visible_name = alias.unwrap_or_else(|| match &src { - MsgSource::Serv { serv } => serv.to_owned(), + MsgSource::Serv { serv, .. } => serv.to_owned(), MsgSource::Chan { chan, .. } => chan.display().to_owned(), MsgSource::User { nick, .. } => nick.to_owned(), }); @@ -414,33 +465,40 @@ impl TUI { } /// Returns index of the new tab if a new tab is created. - pub fn new_server_tab(&mut self, serv: &str, alias: Option) -> Option { - match self.find_serv_tab_idx(serv) { - None => { - let tab_idx = self.tabs.len(); - self.new_tab( - tab_idx, - MsgSource::Serv { - serv: serv.to_owned(), - }, - true, - if cfg!(feature = "desktop-notifications") { - Notifier::Mentions - } else { - Notifier::Off - }, - alias, - ); - Some(tab_idx) - } - Some(_) => None, - } + pub fn new_server_tab(&mut self, serv: &str, alias: Option) -> (ServerId, usize) { + let serv_id = self.server_id_gen.next(); + let tab_idx = self.new_server_tab_with_id(serv_id, serv, alias); + (serv_id, tab_idx) + } + + fn new_server_tab_with_id( + &mut self, + serv_id: ServerId, + serv: &str, + alias: Option, + ) -> usize { + let tab_idx = self.tabs.len(); + self.new_tab( + tab_idx, + MsgSource::Serv { + serv_id, + serv: serv.to_owned(), + }, + true, + if cfg!(feature = "desktop-notifications") { + Notifier::Mentions + } else { + Notifier::Off + }, + alias, + ); + tab_idx } /// Closes a server tab and all associated channel tabs. - pub(crate) fn close_server_tab(&mut self, serv: &str) { - if let Some(tab_idx) = self.find_serv_tab_idx(serv) { - self.tabs.retain(|tab: &Tab| tab.src.serv_name() != serv); + pub(crate) fn close_server_tab(&mut self, serv_id: ServerId) { + if let Some(tab_idx) = self.find_serv_tab_idx(serv_id) { + self.tabs.retain(|tab: &Tab| tab.src.serv_id() != serv_id); if self.active_idx == tab_idx { self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 }); } @@ -449,19 +507,28 @@ impl TUI { } /// Returns index of the new tab if a new tab is created. - pub(crate) fn new_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) -> Option { - match self.find_chan_tab_idx(serv, chan) { - None => match self.find_last_serv_tab_idx(serv) { + pub(crate) fn new_chan_tab( + &mut self, + serv_id: ServerId, + serv: &str, + chan: &ChanNameRef, + ) -> Option { + match self.find_chan_tab_idx(serv_id, chan) { + None => match self.find_last_serv_tab_idx(serv_id) { None => { self.new_server_tab(serv, None); - self.new_chan_tab(serv, chan) + self.new_chan_tab(serv_id, serv, chan) } Some(serv_tab_idx) => { let mut status_val: bool = true; let mut notifier: Option = None; for tab in &self.tabs { - if let MsgSource::Serv { serv: ref serv_ } = tab.src { - if serv == serv_ { + if let MsgSource::Serv { + serv_id: serv_id_, + serv: _, + } = &tab.src + { + if serv_id == *serv_id_ { status_val = tab.widget.is_showing_status(); notifier = Some(tab.notifier); break; @@ -472,6 +539,7 @@ impl TUI { self.new_tab( tab_idx, MsgSource::Chan { + serv_id, serv: serv.to_owned(), chan: chan.to_owned(), }, @@ -492,8 +560,8 @@ impl TUI { } } - pub(crate) fn close_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) { - if let Some(tab_idx) = self.find_chan_tab_idx(serv, chan) { + pub(crate) fn close_chan_tab(&mut self, serv_id: ServerId, chan: &ChanNameRef) { + if let Some(tab_idx) = self.find_chan_tab_idx(serv_id, chan) { self.tabs.remove(tab_idx); if self.active_idx == tab_idx { self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 }); @@ -503,17 +571,18 @@ impl TUI { } /// Returns index of the new tab if a new tab is created. - pub(crate) fn new_user_tab(&mut self, serv: &str, nick: &str) -> Option { - match self.find_user_tab_idx(serv, nick) { - None => match self.find_last_serv_tab_idx(serv) { + fn new_user_tab(&mut self, serv_id: ServerId, serv: &str, nick: &str) -> Option { + match self.find_user_tab_idx(serv_id, nick) { + None => match self.find_last_serv_tab_idx(serv_id) { None => { self.new_server_tab(serv, None); - self.new_user_tab(serv, nick) + self.new_user_tab(serv_id, serv, nick) } Some(tab_idx) => { self.new_tab( tab_idx + 1, MsgSource::User { + serv_id, serv: serv.to_owned(), nick: nick.to_owned(), }, @@ -532,8 +601,8 @@ impl TUI { } } - pub(crate) fn close_user_tab(&mut self, serv: &str, nick: &str) { - if let Some(tab_idx) = self.find_user_tab_idx(serv, nick) { + pub(crate) fn close_user_tab(&mut self, serv_id: ServerId, nick: &str) { + if let Some(tab_idx) = self.find_user_tab_idx(serv_id, nick) { self.tabs.remove(tab_idx); if self.active_idx == tab_idx { self.select_tab(if tab_idx == 0 { 0 } else { tab_idx - 1 }); @@ -986,21 +1055,21 @@ impl TUI { pub(crate) fn switch(&mut self, string: &str) { let mut next_idx = self.active_idx; for (tab_idx, tab) in self.tabs.iter().enumerate() { - match tab.src { - MsgSource::Serv { ref serv } => { + match &tab.src { + MsgSource::Serv { serv, .. } => { if serv.contains(string) { next_idx = tab_idx; break; } } - MsgSource::Chan { ref chan, .. } => { + MsgSource::Chan { chan, .. } => { // TODO: Case sensitive matching here is not ideal if chan.display().contains(string) { next_idx = tab_idx; break; } } - MsgSource::User { ref nick, .. } => { + MsgSource::User { nick, .. } => { if nick.contains(string) { next_idx = tab_idx; break; @@ -1158,10 +1227,14 @@ impl TUI { let mut target_idxs: Vec = Vec::with_capacity(1); match *target { - MsgTarget::Server { serv } => { + MsgTarget::Server { serv_id, serv: _ } => { for (tab_idx, tab) in self.tabs.iter().enumerate() { - if let MsgSource::Serv { serv: ref serv_ } = tab.src { - if serv == serv_ { + if let MsgSource::Serv { + serv_id: serv_id_, + serv: _, + } = &tab.src + { + if serv_id == *serv_id_ { target_idxs.push(tab_idx); break; } @@ -1169,14 +1242,19 @@ impl TUI { } } - MsgTarget::Chan { serv, chan } => { + MsgTarget::Chan { + serv_id, + serv: _, + chan, + } => { for (tab_idx, tab) in self.tabs.iter().enumerate() { if let MsgSource::Chan { - serv: ref serv_, - chan: ref chan_, - } = tab.src + serv_id: serv_id_, + serv: _, + chan: chan_, + } = &tab.src { - if serv == serv_ && chan == chan_ { + if serv_id == *serv_id_ && chan == chan_ { target_idxs.push(tab_idx); break; } @@ -1184,14 +1262,19 @@ impl TUI { } } - MsgTarget::User { serv, nick } => { + MsgTarget::User { + serv_id, + serv: _, + nick, + } => { for (tab_idx, tab) in self.tabs.iter().enumerate() { if let MsgSource::User { - serv: ref serv_, - nick: ref nick_, - } = tab.src + serv_id: serv_id_, + serv: _, + nick: nick_, + } = &tab.src { - if serv == serv_ && nick == nick_ { + if serv_id == *serv_id_ && nick == nick_ { target_idxs.push(tab_idx); break; } @@ -1199,9 +1282,9 @@ impl TUI { } } - MsgTarget::AllServTabs { serv } => { + MsgTarget::AllServTabs { serv_id, serv: _ } => { for (tab_idx, tab) in self.tabs.iter().enumerate() { - if tab.src.serv_name() == serv { + if tab.src.serv_id() == serv_id { target_idxs.push(tab_idx); } } @@ -1226,13 +1309,31 @@ impl TUI { fn maybe_create_tab(&mut self, target: &MsgTarget) -> Option { match *target { - MsgTarget::Server { serv } | MsgTarget::AllServTabs { serv } => { - self.new_server_tab(serv, None) + MsgTarget::Server { serv_id, serv } | MsgTarget::AllServTabs { serv_id, serv } => { + match self.find_serv_tab_idx(serv_id) { + Some(idx) => Some(idx), + None => { + // This case probably happens when user closes a server tab, but the + // connection remains alive for a while (for the shutdown sequence) and we + // get an error. + // TODO: We lost the alias + self.new_server_tab_with_id(serv_id, serv, None); + None + } + } } - MsgTarget::Chan { serv, chan } => self.new_chan_tab(serv, chan), + MsgTarget::Chan { + serv_id, + serv, + chan, + } => self.new_chan_tab(serv_id, serv, chan), - MsgTarget::User { serv, nick } => self.new_user_tab(serv, nick), + MsgTarget::User { + serv_id, + serv, + nick, + } => self.new_user_tab(serv_id, serv, nick), _ => None, } @@ -1311,15 +1412,26 @@ impl TUI { }); } - pub(crate) fn set_topic(&mut self, title: &str, ts: Tm, serv: &str, chan: &ChanNameRef) { - let target = MsgTarget::Chan { serv, chan }; + pub(crate) fn set_topic( + &mut self, + title: &str, + ts: Tm, + serv_id: ServerId, + serv: &str, + chan: &ChanNameRef, + ) { + let target = MsgTarget::Chan { + serv_id, + serv, + chan, + }; self.apply_to_target(&target, false, &|tab: &mut Tab, _| { tab.widget.show_topic(title, Timestamp::from(ts)); }); } - pub(crate) fn clear_nicks(&mut self, serv: &str) { - let target = MsgTarget::AllServTabs { serv }; + pub(crate) fn clear_nicks(&mut self, serv_id: ServerId, serv: &str) { + let target = MsgTarget::AllServTabs { serv_id, serv }; self.apply_to_target(&target, false, &|tab: &mut Tab, _| { tab.widget.clear_nicks(); }); @@ -1356,8 +1468,8 @@ impl TUI { }); } - pub(crate) fn set_nick(&mut self, serv: &str, new_nick: &str) { - let target = MsgTarget::AllServTabs { serv }; + pub(crate) fn set_nick(&mut self, serv_id: ServerId, serv: &str, new_nick: &str) { + let target = MsgTarget::AllServTabs { serv_id, serv }; self.apply_to_target(&target, false, &|tab: &mut Tab, _| { tab.widget.set_nick(new_nick.to_owned()) }); @@ -1368,11 +1480,14 @@ impl TUI { } pub(crate) fn toggle_ignore(&mut self, target: &MsgTarget) { - if let MsgTarget::AllServTabs { serv } = *target { + if let MsgTarget::AllServTabs { serv_id, .. } = target { let mut status_val: bool = false; for tab in &self.tabs { - if let MsgSource::Serv { serv: ref serv_ } = tab.src { - if serv == serv_ { + if let MsgSource::Serv { + serv_id: serv_id_, .. + } = &tab.src + { + if serv_id == serv_id_ { status_val = tab.widget.is_showing_status(); break; } @@ -1389,10 +1504,15 @@ impl TUI { } // TODO: Maybe remove this and add a `create: bool` field to MsgTarget::User - pub(crate) fn user_tab_exists(&self, serv_: &str, nick_: &str) -> bool { + pub(crate) fn user_tab_exists(&self, serv_id_: ServerId, nick_: &str) -> bool { for tab in &self.tabs { - if let MsgSource::User { ref serv, ref nick } = tab.src { - if serv_ == serv && nick_ == nick { + if let MsgSource::User { + serv_id, + serv: _, + nick, + } = &tab.src + { + if serv_id_ == *serv_id && nick_ == nick { return true; } } @@ -1420,10 +1540,10 @@ impl TUI { //////////////////////////////////////////////////////////////////////////// // Helpers - fn find_serv_tab_idx(&self, serv_: &str) -> Option { + fn find_serv_tab_idx(&self, serv_id_: ServerId) -> Option { for (tab_idx, tab) in self.tabs.iter().enumerate() { - if let MsgSource::Serv { ref serv } = tab.src { - if serv_ == serv { + if let MsgSource::Serv { serv_id, .. } = &tab.src { + if serv_id_ == *serv_id { return Some(tab_idx); } } @@ -1431,10 +1551,15 @@ impl TUI { None } - fn find_chan_tab_idx(&self, serv_: &str, chan_: &ChanNameRef) -> Option { + fn find_chan_tab_idx(&self, serv_id_: ServerId, chan_: &ChanNameRef) -> Option { for (tab_idx, tab) in self.tabs.iter().enumerate() { - if let MsgSource::Chan { ref serv, ref chan } = tab.src { - if serv_ == serv && chan_ == chan { + if let MsgSource::Chan { + serv_id, + serv: _, + chan, + } = &tab.src + { + if serv_id_ == *serv_id && chan_ == chan { return Some(tab_idx); } } @@ -1442,10 +1567,15 @@ impl TUI { None } - fn find_user_tab_idx(&self, serv_: &str, nick_: &str) -> Option { + fn find_user_tab_idx(&self, serv_id_: ServerId, nick_: &str) -> Option { for (tab_idx, tab) in self.tabs.iter().enumerate() { - if let MsgSource::User { ref serv, ref nick } = tab.src { - if serv_ == serv && nick_ == nick { + if let MsgSource::User { + serv_id, + serv: _, + nick, + } = &tab.src + { + if serv_id_ == *serv_id && nick_ == nick { return Some(tab_idx); } } @@ -1454,9 +1584,9 @@ impl TUI { } /// Index of the last tab with the given server name. - fn find_last_serv_tab_idx(&self, serv: &str) -> Option { + fn find_last_serv_tab_idx(&self, serv_id: ServerId) -> Option { for (tab_idx, tab) in self.tabs.iter().enumerate().rev() { - if tab.src.serv_name() == serv { + if tab.src.serv_id() == serv_id { return Some(tab_idx); } } diff --git a/crates/tiny/src/main.rs b/crates/tiny/src/main.rs index 4f08f463..e97618a1 100644 --- a/crates/tiny/src/main.rs +++ b/crates/tiny/src/main.rs @@ -104,6 +104,7 @@ fn run( local.block_on(&runtime, async move { // Create TUI task let (tui, rcv_tui_ev) = TUI::run(config_path.clone()); + let mentions_serv_id = tui.mentions_serv_id(); tui.draw(); // Create logger @@ -113,7 +114,10 @@ fn run( // Somehwat hacky -- only tab we have is "mentions" so we show the error there tui_clone.add_client_err_msg( &format!("Logger error: {}", err), - &MsgTarget::Server { serv: "mentions" }, + &MsgTarget::Server { + serv_id: mentions_serv_id, + serv: "mentions", + }, ) }) }; @@ -122,7 +126,10 @@ fn run( Err(LoggerInitError::CouldNotCreateDir { dir_path, err }) => { tui.add_client_err_msg( &format!("Could not create log directory {:?}: {}", dir_path, err), - &MsgTarget::Server { serv: "mentions" }, + &MsgTarget::Server { + serv_id: mentions_serv_id, + serv: "mentions", + }, ); tui.draw(); None diff --git a/crates/tiny/src/ui.rs b/crates/tiny/src/ui.rs index b3820db2..3690ea32 100644 --- a/crates/tiny/src/ui.rs +++ b/crates/tiny/src/ui.rs @@ -209,17 +209,34 @@ pub(crate) fn send_msg( return; } - MsgSource::Chan { ref serv, ref chan } => { - (MsgTarget::Chan { serv, chan }, chan.display()) - } + MsgSource::Chan { + serv_id, + serv, + chan, + } => ( + MsgTarget::Chan { + serv_id, + serv, + chan, + }, + chan.display(), + ), - MsgSource::User { ref serv, ref nick } => { + MsgSource::User { + serv_id, + serv, + nick, + } => { let msg_target = if nick.eq_ignore_ascii_case("nickserv") || nick.eq_ignore_ascii_case("chanserv") { - MsgTarget::Server { serv } + MsgTarget::Server { serv_id, serv } } else { - MsgTarget::User { serv, nick } + MsgTarget::User { + serv_id, + serv, + nick, + } }; (msg_target, nick) }