use crate::discord::ReactionEmoji;
use crate::discord::ids::{
Id,
marker::{ChannelMarker, GuildMarker, MessageMarker, UserMarker},
};
use crate::discord::ReactionUsersInfo;
use crate::tui::keybindings::{KeyChord, LeaderShortcutItem};
use super::{
DashboardState, EmojiReactionItem, FocusPane, MessageUrlItem, PollVotePickerItem,
channel_switcher::ChannelSwitcherState,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) enum LeaderMode {
Root,
Actions,
}
#[derive(Debug, Default)]
pub(super) struct PopupUiState {
pub(super) message_action_menu: Option<MessageActionMenuState>,
pub(super) message_url_picker: Option<MessageUrlPickerState>,
pub(super) message_delete_confirmation: Option<MessageDeleteConfirmationState>,
pub(super) message_pin_confirmation: Option<MessagePinConfirmationState>,
pub(super) options_popup: Option<OptionsPopupState>,
pub(super) attachment_viewer: Option<AttachmentViewerState>,
pub(super) guild_leader_action: Option<GuildLeaderActionState>,
pub(super) channel_leader_action: Option<ChannelLeaderActionState>,
pub(super) member_leader_action: Option<MemberLeaderActionState>,
pub(super) user_profile_popup: Option<UserProfilePopupState>,
pub(super) emoji_reaction_picker: Option<EmojiReactionPickerState>,
pub(super) poll_vote_picker: Option<PollVotePickerState>,
pub(super) reaction_users_popup: Option<ReactionUsersPopupState>,
pub(super) debug_log_popup_open: bool,
pub(super) leader_mode: Option<LeaderMode>,
pub(super) leader_keymap_prefix: Vec<KeyChord>,
pub(super) channel_switcher: Option<ChannelSwitcherState>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct MessageActionMenuState {
pub(super) selected: usize,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MessageUrlPickerState {
pub(super) selected: usize,
pub(super) items: Vec<MessageUrlItem>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MessageDeleteConfirmationState {
pub(super) channel_id: Id<ChannelMarker>,
pub(super) message_id: Id<MessageMarker>,
pub(super) author: String,
pub(super) content: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MessagePinConfirmationState {
pub(super) channel_id: Id<ChannelMarker>,
pub(super) message_id: Id<MessageMarker>,
pub(super) pinned: bool,
pub(super) author: String,
pub(super) content: Option<String>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OptionsCategory {
Display,
Notifications,
Voice,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(super) struct OptionsPopupState {
pub(super) selected: usize,
pub(super) category: Option<OptionsCategory>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct AttachmentViewerState {
pub(super) message_id: Id<MessageMarker>,
pub(super) selected: usize,
pub(super) download_message: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) enum GuildLeaderActionState {
Actions { selected: usize },
MuteDuration { selected: usize },
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct UserProfilePopupState {
pub(super) user_id: Id<UserMarker>,
pub(super) guild_id: Option<Id<GuildMarker>>,
pub(super) load_error: Option<String>,
pub(super) scroll: usize,
pub(super) view_height: usize,
pub(super) total_lines: usize,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct MemberLeaderActionState {
pub(super) user_id: Id<UserMarker>,
pub(super) guild_id: Option<Id<GuildMarker>>,
pub(super) selected: usize,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) enum ChannelLeaderActionState {
Actions {
channel_id: Id<ChannelMarker>,
selected: usize,
},
MuteDuration {
channel_id: Id<ChannelMarker>,
selected: usize,
},
Threads {
channel_id: Id<ChannelMarker>,
selected: usize,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct EmojiReactionPickerState {
pub(super) selected: usize,
pub(super) filter: Option<String>,
pub(super) filter_editing: bool,
pub(super) items: Vec<EmojiReactionItem>,
pub(super) filtered_items: Vec<EmojiReactionItem>,
pub(super) existing_reactions: Vec<ReactionEmoji>,
pub(super) own_reactions: Vec<ReactionEmoji>,
pub(super) guild_id: Option<Id<GuildMarker>>,
pub(super) channel_id: Id<ChannelMarker>,
pub(super) message_id: Id<MessageMarker>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PollVotePickerState {
pub(super) selected: usize,
pub(super) allow_multiselect: bool,
pub(super) channel_id: Id<ChannelMarker>,
pub(super) message_id: Id<MessageMarker>,
pub(super) answers: Vec<PollVotePickerItem>,
}
impl PollVotePickerState {
pub fn answers(&self) -> &[PollVotePickerItem] {
&self.answers
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReactionUsersPopupState {
pub(super) channel_id: Id<ChannelMarker>,
pub(super) message_id: Id<MessageMarker>,
pub(super) reactions: Vec<ReactionUsersInfo>,
pub(super) scroll: usize,
pub(super) view_height: usize,
}
impl ReactionUsersPopupState {
pub fn reactions(&self) -> &[ReactionUsersInfo] {
&self.reactions
}
pub fn scroll(&self) -> usize {
self.scroll
}
pub fn data_line_count(&self) -> usize {
if self.reactions.is_empty() {
return 1;
}
self.reactions
.iter()
.map(|reaction| 1 + reaction.users.len().max(1))
.sum()
}
fn max_scroll(&self) -> usize {
let visible = self.view_height.min(self.data_line_count());
self.data_line_count().saturating_sub(visible)
}
pub(super) fn clamp_scroll(&mut self) {
self.scroll = self.scroll.min(self.max_scroll());
}
}
impl DashboardState {
pub fn is_leader_active(&self) -> bool {
self.popups.leader_mode.is_some()
}
pub fn is_leader_action_mode(&self) -> bool {
self.popups.leader_mode == Some(LeaderMode::Actions)
}
pub fn open_leader(&mut self) {
self.popups.leader_mode = Some(LeaderMode::Root);
self.popups.leader_keymap_prefix = self.options.key_bindings.leader_keymap_prefix();
}
pub(in crate::tui) fn open_keymap_prefix(&mut self, prefix: Vec<KeyChord>) {
self.popups.leader_mode = Some(LeaderMode::Root);
self.popups.leader_keymap_prefix = prefix;
}
pub fn close_leader(&mut self) {
self.popups.leader_mode = None;
self.popups.leader_keymap_prefix.clear();
}
pub(in crate::tui) fn leader_keymap_prefix(&self) -> &[KeyChord] {
&self.popups.leader_keymap_prefix
}
pub(in crate::tui) fn push_leader_keymap_key(&mut self, key: KeyChord) {
self.popups.leader_keymap_prefix.push(key);
}
pub fn leader_keymap_shortcuts(&self) -> Vec<LeaderShortcutItem> {
self.options
.key_bindings
.leader_keymap_children(&self.popups.leader_keymap_prefix)
}
pub(in crate::tui) fn leader_keymap_title(&self) -> String {
self.options
.key_bindings
.keymap_prefix_title(&self.popups.leader_keymap_prefix)
}
pub fn open_leader_actions_for_focused_target(&mut self) {
self.close_all_action_contexts();
match self.navigation.focus {
FocusPane::Guilds => self.open_selected_guild_actions(),
FocusPane::Channels => self.open_selected_channel_actions(),
FocusPane::Messages => self.open_selected_message_actions(),
FocusPane::Members => self.open_selected_member_actions(),
}
self.popups.leader_mode = Some(LeaderMode::Actions);
}
pub fn close_all_action_contexts(&mut self) {
self.popups.message_action_menu = None;
self.popups.guild_leader_action = None;
self.popups.channel_leader_action = None;
self.popups.member_leader_action = None;
}
pub fn is_any_action_context_active(&self) -> bool {
self.popups.message_action_menu.is_some()
|| self.popups.guild_leader_action.is_some()
|| self.popups.channel_leader_action.is_some()
|| self.popups.member_leader_action.is_some()
}
}
impl DashboardState {
pub fn is_channel_leader_action_active(&self) -> bool {
self.popups.channel_leader_action.is_some()
}
pub fn is_guild_leader_action_active(&self) -> bool {
self.popups.guild_leader_action.is_some()
}
pub fn is_channel_action_threads_phase(&self) -> bool {
matches!(
self.popups.channel_leader_action,
Some(ChannelLeaderActionState::Threads { .. })
)
}
pub fn is_channel_action_mute_duration_phase(&self) -> bool {
matches!(
self.popups.channel_leader_action,
Some(ChannelLeaderActionState::MuteDuration { .. })
)
}
pub fn is_guild_action_mute_duration_phase(&self) -> bool {
matches!(
self.popups.guild_leader_action,
Some(GuildLeaderActionState::MuteDuration { .. })
)
}
}