use ratatui::{style::Color, widgets::TableState};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SessionState {
CurrentSaved,
CurrentUnsaved,
CurrentNeverSaved,
Loadable,
}
impl SessionState {
pub fn color(&self, selected: bool) -> Color {
match self {
SessionState::CurrentSaved => Color::Green,
SessionState::CurrentUnsaved => Color::Yellow,
SessionState::CurrentNeverSaved => Color::Red,
SessionState::Loadable => {
if selected {
Color::DarkGray
} else {
Color::Rgb(100, 100, 100) }
}
}
}
pub fn indicator(&self) -> &'static str {
match self {
SessionState::CurrentSaved => "[CURRENT]",
SessionState::CurrentUnsaved => "[CURRENT*]",
SessionState::CurrentNeverSaved => "[CURRENT - UNSAVED]",
SessionState::Loadable => "",
}
}
}
#[derive(Debug, Clone)]
pub struct SessionEntry {
pub name: String,
pub state: SessionState,
pub last_saved: Option<String>, pub created_from_pack: Option<String>, }
#[derive(Debug, Clone)]
pub struct SessionModel {
pub sessions: Vec<SessionEntry>,
pub table_state: TableState,
pub current_session_name: Option<String>,
pub has_unsaved_changes: bool,
pub name_input: Option<String>,
pub current_pack_origin: Option<String>,
}
impl SessionModel {
pub fn new() -> Self {
Self {
sessions: Vec::new(),
table_state: TableState::default(),
current_session_name: None,
has_unsaved_changes: false,
name_input: None,
current_pack_origin: None,
}
}
pub fn mark_dirty(&mut self) {
self.has_unsaved_changes = true;
self.refresh_session_states();
}
pub fn mark_saved(&mut self) {
self.has_unsaved_changes = false;
self.refresh_session_states();
}
pub fn set_current_session(&mut self, name: Option<String>) {
self.current_session_name = name;
self.has_unsaved_changes = false;
self.refresh_session_states();
}
pub fn set_pack_origin(&mut self, pack_path: Option<String>) {
self.current_pack_origin = pack_path;
}
fn is_current(&self, name: &str) -> bool {
self.current_session_name.as_deref() == Some(name)
}
fn determine_state(&self, name: &str, exists_on_disk: bool) -> SessionState {
if self.is_current(name) {
if !exists_on_disk {
SessionState::CurrentNeverSaved
} else if self.has_unsaved_changes {
SessionState::CurrentUnsaved
} else {
SessionState::CurrentSaved
}
} else {
SessionState::Loadable
}
}
pub fn refresh_from_disk(&mut self, available_sessions: Vec<String>) {
let selected_name = self
.table_state
.selected()
.and_then(|i| self.sessions.get(i).map(|s| s.name.clone()));
self.sessions.clear();
if let Some(ref current_name) = self.current_session_name {
if !available_sessions.contains(current_name) {
self.sessions.push(SessionEntry {
name: current_name.clone(),
state: SessionState::CurrentNeverSaved,
last_saved: None,
created_from_pack: self.current_pack_origin.clone(),
});
}
}
for name in available_sessions {
let exists_on_disk = true;
let state = self.determine_state(&name, exists_on_disk);
let session = crate::session::Session::load(&name).ok();
let last_saved = session.as_ref().map(|s| s.last_saved.clone());
let created_from_pack = session.as_ref().and_then(|s| s.created_from_pack.clone());
self.sessions.push(SessionEntry {
name,
state,
last_saved,
created_from_pack,
});
}
self.sessions.sort_by(|a, b| {
match (&a.state, &b.state) {
(
SessionState::CurrentSaved
| SessionState::CurrentUnsaved
| SessionState::CurrentNeverSaved,
SessionState::Loadable,
) => std::cmp::Ordering::Less,
(
SessionState::Loadable,
SessionState::CurrentSaved
| SessionState::CurrentUnsaved
| SessionState::CurrentNeverSaved,
) => std::cmp::Ordering::Greater,
_ => a.name.cmp(&b.name),
}
});
if let Some(name) = selected_name {
if let Some(idx) = self.sessions.iter().position(|s| s.name == name) {
self.table_state.select(Some(idx));
} else if !self.sessions.is_empty() {
self.table_state.select(Some(0));
}
} else if !self.sessions.is_empty() {
self.table_state.select(Some(0));
}
}
fn refresh_session_states(&mut self) {
let new_states: Vec<(usize, SessionState)> = self
.sessions
.iter()
.enumerate()
.map(|(idx, session)| {
let exists_on_disk = session.last_saved.is_some();
let new_state = self.determine_state(&session.name, exists_on_disk);
(idx, new_state)
})
.collect();
for (idx, new_state) in new_states {
if let Some(session) = self.sessions.get_mut(idx) {
session.state = new_state;
}
}
self.sessions.sort_by(|a, b| match (&a.state, &b.state) {
(
SessionState::CurrentSaved
| SessionState::CurrentUnsaved
| SessionState::CurrentNeverSaved,
SessionState::Loadable,
) => std::cmp::Ordering::Less,
(
SessionState::Loadable,
SessionState::CurrentSaved
| SessionState::CurrentUnsaved
| SessionState::CurrentNeverSaved,
) => std::cmp::Ordering::Greater,
_ => a.name.cmp(&b.name),
});
}
pub fn get_selected_session(&self) -> Option<&SessionEntry> {
self.table_state
.selected()
.and_then(|i| self.sessions.get(i))
}
#[allow(dead_code)]
pub fn current_session_index(&self) -> Option<usize> {
self.current_session_name
.as_ref()
.and_then(|name| self.sessions.iter().position(|s| s.name == *name))
}
}
impl Default for SessionModel {
fn default() -> Self {
Self::new()
}
}