diff --git a/src/ui/euph.rs b/src/ui/euph.rs index 0036d78..0c522c7 100644 --- a/src/ui/euph.rs +++ b/src/ui/euph.rs @@ -1,2 +1,3 @@ +mod nick_list; mod popup; pub mod room; diff --git a/src/ui/euph/nick_list.rs b/src/ui/euph/nick_list.rs new file mode 100644 index 0000000..d54a58a --- /dev/null +++ b/src/ui/euph/nick_list.rs @@ -0,0 +1,119 @@ +use std::iter; + +use crossterm::style::{Color, ContentStyle, Stylize}; +use euphoxide::api::{SessionType, SessionView}; +use euphoxide::conn::Joined; +use toss::styled::Styled; + +use crate::euph; +use crate::ui::widgets::background::Background; +use crate::ui::widgets::empty::Empty; +use crate::ui::widgets::list::{List, ListState}; +use crate::ui::widgets::text::Text; +use crate::ui::widgets::BoxedWidget; + +pub fn widget(state: &ListState, joined: &Joined) -> BoxedWidget { + let mut list = state.widget(); + render_rows(&mut list, joined); + list.into() +} + +fn render_rows(list: &mut List, joined: &Joined) { + let mut people = vec![]; + let mut bots = vec![]; + let mut lurkers = vec![]; + let mut nurkers = vec![]; + + let mut sessions = iter::once(&joined.session) + .chain(joined.listing.values()) + .collect::>(); + sessions.sort_unstable_by_key(|s| &s.name); + for sess in sessions { + match sess.id.session_type() { + Some(SessionType::Bot) if sess.name.is_empty() => nurkers.push(sess), + Some(SessionType::Bot) => bots.push(sess), + _ if sess.name.is_empty() => lurkers.push(sess), + _ => people.push(sess), + } + } + + people.sort_unstable_by_key(|s| (&s.name, &s.session_id)); + bots.sort_unstable_by_key(|s| (&s.name, &s.session_id)); + lurkers.sort_unstable_by_key(|s| &s.session_id); + nurkers.sort_unstable_by_key(|s| &s.session_id); + + render_section(list, "People", &people, &joined.session); + render_section(list, "Bots", &bots, &joined.session); + render_section(list, "Lurkers", &lurkers, &joined.session); + render_section(list, "Nurkers", &nurkers, &joined.session); +} + +fn render_section( + list: &mut List, + name: &str, + sessions: &[&SessionView], + own_session: &SessionView, +) { + if sessions.is_empty() { + return; + } + + let heading_style = ContentStyle::new().bold(); + + if !list.is_empty() { + list.add_unsel(Empty::new()); + } + + let row = Styled::new_plain(" ") + .then(name, heading_style) + .then_plain(format!(" ({})", sessions.len())); + list.add_unsel(Text::new(row)); + + for session in sessions { + render_row(list, session, own_session); + } +} + +fn render_row(list: &mut List, session: &SessionView, own_session: &SessionView) { + let id = session.session_id.clone(); + + let (name, style, style_inv) = if session.name.is_empty() { + let name = "lurk"; + let style = ContentStyle::default().grey(); + let style_inv = ContentStyle::default().black().on_grey(); + (name, style, style_inv) + } else { + let name = &session.name as &str; + let (r, g, b) = euph::nick_color(name); + let color = Color::Rgb { r, g, b }; + let style = ContentStyle::default().bold().with(color); + let style_inv = ContentStyle::default().bold().black().on(color); + (name, style, style_inv) + }; + + let perms = if session.is_staff { + "!" + } else if session.is_manager { + "*" + } else if session.id.session_type() == Some(SessionType::Account) { + "~" + } else { + "" + }; + + let owner = if session.session_id == own_session.session_id { + ">" + } else { + " " + }; + + let normal = Styled::new_plain(owner).then(name, style).then_plain(perms); + let selected = Styled::new_plain(owner) + .then(name, style_inv) + .then_plain(perms); + list.add_sel( + id, + Text::new(normal), + Background::new(Text::new(selected)).style(style_inv), + ); +} diff --git a/src/ui/euph/room.rs b/src/ui/euph/room.rs index 6e48378..88b2965 100644 --- a/src/ui/euph/room.rs +++ b/src/ui/euph/room.rs @@ -1,10 +1,9 @@ use std::collections::VecDeque; -use std::iter; use std::sync::Arc; use crossterm::event::KeyCode; -use crossterm::style::{Color, ContentStyle, Stylize}; -use euphoxide::api::{Data, PacketType, SessionType, SessionView, Snowflake}; +use crossterm::style::{ContentStyle, Stylize}; +use euphoxide::api::{Data, PacketType, Snowflake}; use euphoxide::conn::{Joined, Joining, Status}; use parking_lot::FairMutex; use tokio::sync::oneshot::error::TryRecvError; @@ -17,14 +16,12 @@ use crate::macros::{ok_or_return, some_or_return}; use crate::store::MsgStore; use crate::ui::chat::{ChatState, Reaction}; use crate::ui::input::{key, InputEvent, KeyBindingsList, KeyEvent}; -use crate::ui::widgets::background::Background; use crate::ui::widgets::border::Border; use crate::ui::widgets::cursor::Cursor; use crate::ui::widgets::editor::EditorState; -use crate::ui::widgets::empty::Empty; use crate::ui::widgets::join::{HJoin, Segment, VJoin}; use crate::ui::widgets::layer::Layer; -use crate::ui::widgets::list::{List, ListState}; +use crate::ui::widgets::list::ListState; use crate::ui::widgets::padding::Padding; use crate::ui::widgets::popup::Popup; use crate::ui::widgets::text::Text; @@ -32,6 +29,7 @@ use crate::ui::widgets::BoxedWidget; use crate::ui::{util, UiEvent}; use crate::vault::EuphVault; +use super::nick_list; use super::popup::RoomPopup; enum State { @@ -249,7 +247,7 @@ impl EuphRoom { ])) .expanding(true), Segment::new(Border::new( - Padding::new(self.nick_list_widget(joined)).right(1), + Padding::new(nick_list::widget(&self.nick_list, joined)).right(1), )), ]) .into() @@ -290,116 +288,6 @@ impl EuphRoom { Text::new(info).into() } - fn render_nick_list_row( - list: &mut List, - session: &SessionView, - own_session: &SessionView, - ) { - let id = session.session_id.clone(); - - let (name, style, style_inv) = if session.name.is_empty() { - let name = "lurk"; - let style = ContentStyle::default().grey(); - let style_inv = ContentStyle::default().black().on_grey(); - (name, style, style_inv) - } else { - let name = &session.name as &str; - let (r, g, b) = euph::nick_color(name); - let color = Color::Rgb { r, g, b }; - let style = ContentStyle::default().bold().with(color); - let style_inv = ContentStyle::default().bold().black().on(color); - (name, style, style_inv) - }; - - let perms = if session.is_staff { - "!" - } else if session.is_manager { - "*" - } else if session.id.session_type() == Some(SessionType::Account) { - "~" - } else { - "" - }; - - let owner = if session.session_id == own_session.session_id { - ">" - } else { - " " - }; - - let normal = Styled::new_plain(owner).then(name, style).then_plain(perms); - let selected = Styled::new_plain(owner) - .then(name, style_inv) - .then_plain(perms); - list.add_sel( - id, - Text::new(normal), - Background::new(Text::new(selected)).style(style_inv), - ); - } - - fn render_nick_list_section( - list: &mut List, - name: &str, - sessions: &[&SessionView], - own_session: &SessionView, - ) { - if sessions.is_empty() { - return; - } - - let heading_style = ContentStyle::new().bold(); - - if !list.is_empty() { - list.add_unsel(Empty::new()); - } - - let row = Styled::new_plain(" ") - .then(name, heading_style) - .then_plain(format!(" ({})", sessions.len())); - list.add_unsel(Text::new(row)); - - for session in sessions { - Self::render_nick_list_row(list, session, own_session); - } - } - - fn render_nick_list_rows(list: &mut List, joined: &Joined) { - let mut people = vec![]; - let mut bots = vec![]; - let mut lurkers = vec![]; - let mut nurkers = vec![]; - - let mut sessions = iter::once(&joined.session) - .chain(joined.listing.values()) - .collect::>(); - sessions.sort_unstable_by_key(|s| &s.name); - for sess in sessions { - match sess.id.session_type() { - Some(SessionType::Bot) if sess.name.is_empty() => nurkers.push(sess), - Some(SessionType::Bot) => bots.push(sess), - _ if sess.name.is_empty() => lurkers.push(sess), - _ => people.push(sess), - } - } - - people.sort_unstable_by_key(|s| (&s.name, &s.session_id)); - bots.sort_unstable_by_key(|s| (&s.name, &s.session_id)); - lurkers.sort_unstable_by_key(|s| &s.session_id); - nurkers.sort_unstable_by_key(|s| &s.session_id); - - Self::render_nick_list_section(list, "People", &people, &joined.session); - Self::render_nick_list_section(list, "Bots", &bots, &joined.session); - Self::render_nick_list_section(list, "Lurkers", &lurkers, &joined.session); - Self::render_nick_list_section(list, "Nurkers", &nurkers, &joined.session); - } - - fn nick_list_widget(&self, joined: &Joined) -> BoxedWidget { - let mut list = self.nick_list.widget(); - Self::render_nick_list_rows(&mut list, joined); - list.into() - } - fn nick_char(c: char) -> bool { c != '\n' }