use std::time::Instant;
use crate::discord::ids::{
Id,
marker::{ChannelMarker, UserMarker},
};
use crate::discord::{
ActivityInfo, AttachmentDownloadId, DownloadAttachmentSource, MediaPlaybackRequestId,
StreamCaptureTargetsRequestId, VoiceScope,
};
use super::{AttachmentDownloadProgressView, DashboardState, ToastKind};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct ToastMessage {
pub(super) text: String,
pub(super) kind: ToastKind,
pub(super) expires_at: Option<Instant>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct VoiceConnectionUiState {
pub(super) scope: VoiceScope,
pub(super) channel_id: Option<Id<ChannelMarker>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct AttachmentDownloadUiState {
pub(super) id: AttachmentDownloadId,
pub(super) filename: String,
pub(super) downloaded_bytes: u64,
pub(super) total_bytes: Option<u64>,
pub(super) source: DownloadAttachmentSource,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct MediaPlaybackPreparingUiState {
pub(super) request_id: MediaPlaybackRequestId,
pub(super) url: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct StreamPlaybackUiTarget {
pub(super) scope: VoiceScope,
pub(super) channel_id: Id<ChannelMarker>,
pub(super) user_id: Id<UserMarker>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct StreamBroadcastUiTarget {
pub(super) scope: VoiceScope,
pub(super) channel_id: Id<ChannelMarker>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(super) struct StreamCaptureTargetsRequest {
pub(super) request_id: StreamCaptureTargetsRequestId,
pub(super) target: StreamBroadcastUiTarget,
}
impl StreamBroadcastUiTarget {
pub(super) fn matches(&self, scope: VoiceScope, channel_id: Id<ChannelMarker>) -> bool {
self.scope == scope && self.channel_id == channel_id
}
}
#[derive(Debug, Default)]
pub(super) struct RuntimeUiState {
pub(super) toast_message: Option<ToastMessage>,
pub(super) media_playback_preparing: Option<MediaPlaybackPreparingUiState>,
pub(super) stream_playback_preparing: Option<StreamPlaybackUiTarget>,
pub(super) active_stream_playback: Option<StreamPlaybackUiTarget>,
pub(super) stream_broadcast_preparing: Option<StreamBroadcastUiTarget>,
pub(super) active_stream_broadcast: Option<StreamBroadcastUiTarget>,
pub(super) stream_capture_targets_request: Option<StreamCaptureTargetsRequest>,
pub(super) gateway_error: Option<String>,
pub(super) voice_connection: Option<VoiceConnectionUiState>,
pub(super) open_composer_in_editor_requested: bool,
pub(super) open_forum_post_body_in_editor_requested: bool,
pub(super) paste_clipboard_requested: bool,
pub(super) clipboard_paste_pending: bool,
pub(super) copy_text_requested: Option<(String, &'static str)>,
pub(super) attachment_downloads: Vec<AttachmentDownloadUiState>,
pub(super) next_attachment_download_id: u64,
pub(super) next_media_playback_request_id: u64,
pub(super) next_stream_capture_targets_request_id: u64,
pub(super) animation_frame: usize,
pub(super) should_quit: bool,
pub(super) should_sign_out: bool,
pub(super) terminal_focus_lost: bool,
pub(super) detected_rich_presence: Vec<ActivityInfo>,
}
impl DashboardState {
pub fn quit(&mut self) {
self.runtime.should_quit = true;
}
pub(in crate::tui) fn sign_out(&mut self) {
self.runtime.should_sign_out = true;
}
pub fn should_quit(&self) -> bool {
self.runtime.should_quit || self.runtime.should_sign_out
}
pub(in crate::tui) fn should_sign_out(&self) -> bool {
self.runtime.should_sign_out
}
pub(in crate::tui) fn animation_frame(&self) -> usize {
self.runtime.animation_frame
}
pub(in crate::tui) fn advance_animation_frame(&mut self) {
self.runtime.animation_frame = self.runtime.animation_frame.wrapping_add(1);
}
pub(in crate::tui) fn needs_animation_frame(&self) -> bool {
self.terminal_focused()
&& (self.notification_inbox_has_visible_loading_indicator()
|| self.search_popup_has_visible_loading_indicator()
|| self.selected_forum_posts_loading())
}
pub fn set_terminal_focused(&mut self, focused: bool) {
self.runtime.terminal_focus_lost = !focused;
}
pub(super) fn terminal_focused(&self) -> bool {
!self.runtime.terminal_focus_lost
}
pub(in crate::tui) fn set_detected_rich_presence(&mut self, activities: Vec<ActivityInfo>) {
self.runtime.detected_rich_presence = activities;
}
pub(in crate::tui) fn detected_rich_presence(&self) -> &[ActivityInfo] {
&self.runtime.detected_rich_presence
}
pub(in crate::tui) fn next_attachment_download_id(&mut self) -> AttachmentDownloadId {
let id = AttachmentDownloadId::new(self.runtime.next_attachment_download_id);
self.runtime.next_attachment_download_id =
self.runtime.next_attachment_download_id.saturating_add(1);
id
}
pub(in crate::tui) fn next_media_playback_request_id(&mut self) -> MediaPlaybackRequestId {
let id = MediaPlaybackRequestId::new(self.runtime.next_media_playback_request_id);
self.runtime.next_media_playback_request_id = self
.runtime
.next_media_playback_request_id
.saturating_add(1);
id
}
pub(in crate::tui) fn begin_stream_capture_targets_request(
&mut self,
scope: VoiceScope,
channel_id: Id<ChannelMarker>,
) -> StreamCaptureTargetsRequestId {
let request_id =
StreamCaptureTargetsRequestId::new(self.runtime.next_stream_capture_targets_request_id);
self.runtime.next_stream_capture_targets_request_id = self
.runtime
.next_stream_capture_targets_request_id
.saturating_add(1);
self.runtime.stream_capture_targets_request = Some(StreamCaptureTargetsRequest {
request_id,
target: StreamBroadcastUiTarget { scope, channel_id },
});
request_id
}
pub(in crate::tui) fn record_attachment_download_started(
&mut self,
id: AttachmentDownloadId,
filename: String,
total_bytes: Option<u64>,
source: DownloadAttachmentSource,
) {
if let Some(download) = self
.runtime
.attachment_downloads
.iter_mut()
.find(|download| download.id == id)
{
download.filename = filename;
download.downloaded_bytes = 0;
download.total_bytes = total_bytes;
download.source = source;
return;
}
self.runtime
.attachment_downloads
.push(AttachmentDownloadUiState {
id,
filename,
downloaded_bytes: 0,
total_bytes,
source,
});
}
pub(in crate::tui) fn record_attachment_download_progress(
&mut self,
id: AttachmentDownloadId,
downloaded_bytes: u64,
total_bytes: Option<u64>,
) {
if let Some(download) = self
.runtime
.attachment_downloads
.iter_mut()
.find(|download| download.id == id)
{
download.downloaded_bytes = downloaded_bytes;
if total_bytes.is_some() {
download.total_bytes = total_bytes;
}
}
}
pub(in crate::tui) fn remove_attachment_download(
&mut self,
id: AttachmentDownloadId,
) -> Option<String> {
let index = self
.runtime
.attachment_downloads
.iter()
.position(|download| download.id == id)?;
Some(self.runtime.attachment_downloads.remove(index).filename)
}
pub fn attachment_downloads(&self) -> Vec<AttachmentDownloadProgressView> {
self.runtime
.attachment_downloads
.iter()
.map(|download| AttachmentDownloadProgressView {
id: download.id,
filename: download.filename.clone(),
downloaded_bytes: download.downloaded_bytes,
total_bytes: download.total_bytes,
})
.collect()
}
}