Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/discord/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,10 @@ pub enum AppEvent {
channel_id: Id<ChannelMarker>,
message_id: Id<MessageMarker>,
},
GuildMemberListCounts {
guild_id: Id<GuildMarker>,
online: u32,
},
GuildMemberUpsert {
guild_id: Id<GuildMarker>,
member: MemberInfo,
Expand Down
13 changes: 12 additions & 1 deletion src/discord/gateway/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,11 +1069,22 @@ fn parse_member_list_update(data: &Value) -> Vec<AppEvent> {
return Vec::new();
};

let mut events = Vec::new();

if let Some(groups) = data.get("groups").and_then(Value::as_array) {
let online = groups
.iter()
.filter(|g| g.get("id").and_then(Value::as_str) != Some("offline"))
.filter_map(|g| g.get("count").and_then(Value::as_u64))
.map(|c| c as u32)
.sum();
events.push(AppEvent::GuildMemberListCounts { guild_id, online });
}

// A single GUILD_MEMBER_LIST_UPDATE event can carry SYNC ops for several
// ranges (e.g. `[0,99]` plus `[100,199]`). We previously dropped every
// SYNC whose range did not start at zero, which left members past the
// first chunk invisible in larger guilds.
let mut events = Vec::new();
for op in ops {
match op.get("op").and_then(Value::as_str) {
Some("SYNC") => {
Expand Down
7 changes: 7 additions & 0 deletions src/discord/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub struct GuildState {
pub id: Id<GuildMarker>,
pub name: String,
pub member_count: Option<u64>,
pub online_count: Option<u32>,
/// Snowflake of the guild owner. Owners short-circuit permission checks
/// (they always see every channel). `None` until the GUILD_CREATE /
/// GUILD_UPDATE payload supplies it.
Expand Down Expand Up @@ -407,6 +408,7 @@ impl DiscordState {
name: name.clone(),
member_count: *member_count,
owner_id: *owner_id,
online_count: None,
},
);

Expand Down Expand Up @@ -664,6 +666,11 @@ impl DiscordState {
}
self.refresh_message_author_display_name(*guild_id, member);
}
AppEvent::GuildMemberListCounts { guild_id, online } => {
if let Some(guild) = self.guilds.get_mut(guild_id) {
guild.online_count = Some(*online);
}
}
AppEvent::GuildMemberUpsert { guild_id, member } => {
let entry = self.members.entry(*guild_id).or_default();
let previous_status = entry.get(&member.user_id).map(|m| m.status);
Expand Down
8 changes: 5 additions & 3 deletions src/tui/state/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ mod fixtures;

use fixtures::*;

use ratatui::text::Line;

use crate::{
config::{DisplayOptions, ImagePreviewQualityPreset},
discord::ids::{
Expand Down Expand Up @@ -819,7 +821,7 @@ fn member_panel_title_separates_loaded_and_total_members() {
});
state.confirm_selected_guild();

assert_eq!(state.member_panel_title(), "Members 1/100 loaded");
assert_eq!(state.member_panel_title(), Line::from(" Members "));
Comment thread
lisk77 marked this conversation as resolved.
Outdated
Comment thread
lisk77 marked this conversation as resolved.
Outdated
assert_eq!(state.flattened_members().len(), 1);
}

Expand All @@ -838,7 +840,7 @@ fn member_panel_title_stays_plain_without_guild_total_or_in_direct_messages() {
owner_id: None,
});
guild_state.confirm_selected_guild();
assert_eq!(guild_state.member_panel_title(), "Members");
assert_eq!(guild_state.member_panel_title(), Line::from(" Members "));

let mut dm_state = DashboardState::new();
dm_state.push_event(AppEvent::ChannelUpsert(ChannelInfo {
Expand All @@ -858,7 +860,7 @@ fn member_panel_title_stays_plain_without_guild_total_or_in_direct_messages() {
permission_overwrites: Vec::new(),
}));
dm_state.confirm_selected_guild();
assert_eq!(dm_state.member_panel_title(), "Members");
assert_eq!(dm_state.member_panel_title(), Line::from(" Members "));
}

#[test]
Expand Down
44 changes: 33 additions & 11 deletions src/tui/state/user.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
use std::collections::HashSet;

use ratatui::{
layout::Alignment,
style::{Color, Style},
text::{Line, Span},
};

use crate::discord::ids::{
Id,
marker::{GuildMarker, UserMarker},
Expand Down Expand Up @@ -370,20 +376,24 @@ impl DashboardState {
self.discord.member_role_color(guild_id, member.user_id())
}

pub fn member_panel_title(&self) -> String {
pub fn member_panel_title(&self) -> Line<'static> {
let Some(guild_id) = self.selected_guild_id() else {
return "Members".to_owned();
return Line::from(" Members ");
};
let Some(member_count) = self
.discord
.guild(guild_id)
.and_then(|guild| guild.member_count)
else {
return "Members".to_owned();
let guild = self.discord.guild(guild_id);
let Some(online) = guild.and_then(|g| g.online_count) else {
return Line::from(" Members ");
};

let loaded = self.discord.members_for_guild(guild_id).len();
format!("Members {loaded}/{member_count} loaded")
let total = guild.and_then(|g| g.member_count).unwrap_or(0);
Line::from(vec![
Span::styled("●", Style::default().fg(Color::Green)),
Span::raw(format!(
" {} ○ {}",
fmt_with_separators(online as u64),
fmt_with_separators(total)
)),
])
.alignment(Alignment::Center)
}

fn selected_channel_recipient_group(&self) -> Vec<MemberGroup<'_>> {
Expand All @@ -397,3 +407,15 @@ impl DashboardState {
flatten_member_groups(self.members_grouped())
}
}

fn fmt_with_separators(n: u64) -> String {
let s = n.to_string();
let mut result = String::new();
for (i, c) in s.chars().rev().enumerate() {
if i > 0 && i % 3 == 0 {
result.push(',');
}
result.push(c);
}
result.chars().rev().collect()
}
11 changes: 11 additions & 0 deletions src/tui/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,5 +373,16 @@ fn panel_block_owned(title: String, focused: bool) -> Block<'static> {
.title_style(Style::default().fg(Color::White).bold())
}

pub(super) fn panel_block_line(title: Line<'static>, focused: bool) -> Block<'static> {
let border = if focused { ACCENT } else { Color::DarkGray };

Block::default()
.title(title)
.borders(Borders::ALL)
.border_type(BorderType::Plain)
.border_style(Style::default().fg(border))
.title_style(Style::default().fg(Color::White).bold())
}

#[cfg(test)]
mod tests;
4 changes: 2 additions & 2 deletions src/tui/ui/panes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use super::{
active_text_style, channel_prefix, channel_unread_decoration, dm_presence_dot_span,
highlight_style,
layout::{composer_inner_width, panel_scrollbar_area},
panel_block, panel_block_owned, panel_content_height, render_vertical_scrollbar,
panel_block, panel_block_line, panel_content_height, render_vertical_scrollbar,
selection_marker, styled_list_item,
types::{ACCENT, DIM, MessageAreas},
};
Expand Down Expand Up @@ -613,7 +613,7 @@ pub(super) fn render_members(frame: &mut Frame, area: Rect, state: &DashboardSta

frame.render_widget(
Paragraph::new(lines)
.block(panel_block_owned(state.member_panel_title(), focused))
.block(panel_block_line(state.member_panel_title(), focused))
.wrap(Wrap { trim: false }),
area,
);
Expand Down