use std::collections::BTreeMap;
use dioxus::prelude::*;
use crate::dashboard::common::{
seed_message_states, FolderId, MessageState, MessageTag, TabId, FOLDERS,
};
use super::filters::{message_matches_filters, message_matches_folder};
#[derive(Clone, PartialEq, Store)]
pub(super) struct EmailClientState {
pub messages: BTreeMap<String, MessageState>,
pub message_order: Vec<String>,
pub active_folder: FolderId,
pub active_tab: TabId,
pub search_query: String,
pub selected_tags: Vec<MessageTag>,
pub selected_id: String,
pub read_open: bool,
pub compose_open: bool,
pub compose_to: String,
pub compose_subject: String,
pub compose_body: String,
}
impl EmailClientState {
pub fn new() -> Self {
let seeded = seed_message_states();
let message_order = seeded.iter().map(|message| message.uid.clone()).collect();
let messages = seeded
.into_iter()
.map(|message| (message.uid.clone(), message))
.collect();
Self {
messages,
message_order,
active_folder: FolderId::Inbox,
active_tab: TabId::All,
search_query: String::new(),
selected_tags: Vec::new(),
selected_id: String::from("0#0"),
read_open: false,
compose_open: false,
compose_to: String::new(),
compose_subject: String::new(),
compose_body: String::new(),
}
}
}
#[store(pub)]
impl<Lens> Store<EmailClientState, Lens> {
fn active_folder_label(&self) -> &'static str {
let active_folder = self.active_folder().cloned();
FOLDERS
.iter()
.find(|folder| folder.id == active_folder)
.map(|folder| folder.label)
.unwrap_or("Inbox")
}
fn visible_message_ids(&self) -> Vec<String> {
let active_folder = self.active_folder().cloned();
let active_tab = self.active_tab().cloned();
let search_query = self.search_query().cloned();
let selected_tags = self.selected_tags().cloned();
let messages_store = self.messages();
let order_store = self.message_order();
let messages = messages_store.read();
let order = order_store.read();
order
.iter()
.filter(|uid| {
messages.get(uid.as_str()).is_some_and(|message| {
message_matches_filters(
message,
active_folder,
active_tab,
search_query.as_str(),
&selected_tags,
)
})
})
.cloned()
.collect()
}
fn selected_message_uid(&self, visible_ids: &[String]) -> Option<String> {
let selected_id = self.selected_id().cloned();
if visible_ids.iter().any(|uid| uid == &selected_id) {
return Some(selected_id);
}
visible_ids.first().cloned()
}
fn selected_message_index(&self, selected_uid: Option<&str>, visible_ids: &[String]) -> usize {
selected_uid
.and_then(|selected_uid| {
visible_ids
.iter()
.position(|uid| uid == selected_uid)
.map(|index| index + 1)
})
.unwrap_or(0)
}
fn folder_count(&self, folder_id: FolderId) -> u32 {
let messages_store = self.messages();
let count = messages_store
.read()
.values()
.filter(|message| message_matches_folder(message, folder_id))
.count() as u32;
count
}
fn tab_count(&self, tab_id: TabId, visible_query: &str, selected_tags: &[MessageTag]) -> u32 {
let active_folder = self.active_folder().cloned();
let messages_store = self.messages();
let count = messages_store
.read()
.values()
.filter(|message| {
message_matches_filters(
message,
active_folder,
tab_id,
visible_query,
selected_tags,
)
})
.count() as u32;
count
}
fn set_active_folder(&mut self, folder_id: FolderId) {
self.active_folder().set(folder_id);
self.close_read_pane();
}
fn set_active_tab(&mut self, tab_id: TabId) {
self.active_tab().set(tab_id);
self.close_read_pane();
}
fn set_search_query(&mut self, query: String) {
self.search_query().set(query);
self.close_read_pane();
}
fn set_selected_tags(&mut self, tags: Vec<MessageTag>) {
self.selected_tags().set(tags);
self.close_read_pane();
}
fn select_message(&mut self, uid: String) {
self.selected_id().set(uid);
self.read_open().set(true);
}
fn close_read_pane(&mut self) {
self.read_open().set(false);
}
fn open_compose(&mut self) {
self.compose_open().set(true);
}
fn set_compose_open(&mut self, open: bool) {
self.compose_open().set(open);
}
fn discard_compose(&mut self) {
self.compose_open().set(false);
self.compose_to().set(String::new());
self.compose_subject().set(String::new());
self.compose_body().set(String::new());
}
fn set_compose_to(&mut self, value: String) {
self.compose_to().set(value);
}
fn set_compose_subject(&mut self, value: String) {
self.compose_subject().set(value);
}
fn set_compose_body(&mut self, value: String) {
self.compose_body().set(value);
}
fn archive_message(&mut self, uid: String) {
self.update_message(uid.clone(), |message| {
message.folder_id = FolderId::Archive;
message.unread = false;
});
self.close_if_selected(&uid);
}
fn snooze_message(&mut self, uid: String) {
self.update_message(uid.clone(), |message| {
message.snoozed = true;
});
self.close_if_selected(&uid);
}
fn delete_message(&mut self, uid: String) {
self.update_message(uid.clone(), |message| {
message.folder_id = FolderId::Trash;
message.unread = false;
});
self.close_if_selected(&uid);
}
fn toggle_message_flag(&mut self, uid: String) {
self.update_message(uid, |message| {
message.flagged = !message.flagged;
});
}
fn toggle_message_star(&mut self, uid: String) {
self.update_message(uid, |message| {
message.starred = !message.starred;
});
}
fn move_message_to_trash(&mut self, uid: String) {
self.update_message(uid.clone(), |message| {
message.folder_id = FolderId::Trash;
});
self.close_if_selected(&uid);
}
fn remove_message_tag(&mut self, uid: String, tag: MessageTag) {
self.update_message(uid, |message| {
message.tags.retain(|current| *current != tag);
});
}
fn set_message_tags(&mut self, uid: String, tags: Vec<MessageTag>) {
self.update_message(uid, |message| {
message.tags = tags;
});
}
fn update_message(&mut self, uid: String, update: impl FnOnce(&mut MessageState)) {
if let Some(mut message) = self.messages().get(uid) {
let mut message = message.write();
update(&mut message);
}
}
fn close_if_selected(&mut self, uid: &str) {
if self.selected_id().cloned() == uid {
self.close_read_pane();
}
}
}