use crate::discord::ChannelState;
use crate::discord::ids::{
Id,
marker::{ChannelMarker, GuildMarker},
};
use super::presentation::sort_channels;
use super::{ActiveGuildScope, DashboardState};
impl DashboardState {
pub fn member_list_subscription_target(&self) -> Option<(Id<GuildMarker>, Id<ChannelMarker>)> {
let guild_id = match self.navigation.active_guild {
ActiveGuildScope::Guild(guild_id) => guild_id,
ActiveGuildScope::DirectMessages | ActiveGuildScope::Unset => return None,
};
let channel_id = self
.navigation
.active_channel_id
.filter(|channel_id| {
self.discord
.cache
.channel(*channel_id)
.is_some_and(|channel| self.is_member_list_subscription_channel(channel))
})
.or_else(|| self.guild_member_list_channel(guild_id))?;
Some((guild_id, channel_id))
}
pub fn member_subscription_top_bucket(&self) -> u32 {
let scroll = u32::try_from(self.navigation.member_scroll).unwrap_or(u32::MAX);
let view = u32::try_from(self.navigation.member_view_height).unwrap_or(0);
scroll.saturating_add(view) / 100
}
pub fn member_subscription_ranges(&self) -> Vec<(u32, u32)> {
let top = self.member_subscription_top_bucket();
if top <= 2 {
return (0..=top).map(|b| (b * 100, b * 100 + 99)).collect();
}
let near_start = top.saturating_sub(1);
vec![
(0, 99),
(near_start * 100, near_start * 100 + 99),
(top * 100, top * 100 + 99),
]
}
pub fn guild_member_list_channel(
&self,
guild_id: Id<GuildMarker>,
) -> Option<Id<ChannelMarker>> {
let mut candidates: Vec<&ChannelState> = self
.discord
.viewable_channels_for_guild(Some(guild_id))
.into_iter()
.filter(|channel| self.is_member_list_subscription_channel(channel))
.collect();
sort_channels(&mut candidates);
candidates.first().map(|channel| channel.id)
}
fn is_member_list_subscription_channel(&self, channel: &ChannelState) -> bool {
!channel.is_category()
&& !channel.is_thread()
&& !matches!(channel.kind.as_str(), "voice" | "GuildVoice")
&& self.discord.cache.can_view_channel(channel)
}
}