use kimun_core::nfs::VaultPath;
use kimun_core::{ResultType, SearchResult};
use ratatui::style::{Modifier, Style};
use ratatui::widgets::ListItem;
use crate::components::rich_row::RichRow;
use crate::settings::icons::Icons;
use crate::settings::themes::Theme;
use crate::settings::{SortFieldSetting, SortOrderSetting};
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum SortField {
Name,
Title,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum SortOrder {
Ascending,
Descending,
}
impl From<SortFieldSetting> for SortField {
fn from(s: SortFieldSetting) -> Self {
match s {
SortFieldSetting::Name => Self::Name,
SortFieldSetting::Title => Self::Title,
}
}
}
impl From<SortOrderSetting> for SortOrder {
fn from(s: SortOrderSetting) -> Self {
match s {
SortOrderSetting::Ascending => Self::Ascending,
SortOrderSetting::Descending => Self::Descending,
}
}
}
impl From<SortField> for SortFieldSetting {
fn from(s: SortField) -> Self {
match s {
SortField::Name => Self::Name,
SortField::Title => Self::Title,
}
}
}
impl From<SortOrder> for SortOrderSetting {
fn from(s: SortOrder) -> Self {
match s {
SortOrder::Ascending => Self::Ascending,
SortOrder::Descending => Self::Descending,
}
}
}
impl SortField {
pub fn label(self) -> char {
match self {
Self::Name => 'N',
Self::Title => 'T',
}
}
pub fn cycle(self) -> Self {
match self {
Self::Name => Self::Title,
Self::Title => Self::Name,
}
}
}
impl SortOrder {
pub fn label(self) -> char {
match self {
Self::Ascending => '↑',
Self::Descending => '↓',
}
}
pub fn toggle(self) -> Self {
match self {
Self::Ascending => Self::Descending,
Self::Descending => Self::Ascending,
}
}
}
#[derive(Clone)]
pub enum FileListEntry {
Up {
parent: VaultPath,
},
Note {
path: VaultPath,
title: String,
filename: String,
journal_date: Option<String>,
},
Directory {
path: VaultPath,
name: String,
},
Attachment {
path: VaultPath,
filename: String,
},
CreateNote {
filename: String,
path: VaultPath,
},
}
impl FileListEntry {
pub fn from_result(result: SearchResult, journal_date: Option<String>) -> Self {
let filename = result.path.get_parent_path().1;
match result.rtype {
ResultType::Note(data) => {
let title = if data.title.trim().is_empty() {
"<no title>".to_string()
} else {
data.title
};
Self::Note {
path: result.path,
title,
filename,
journal_date,
}
}
ResultType::Directory => Self::Directory {
path: result.path,
name: filename,
},
ResultType::Attachment => Self::Attachment {
path: result.path,
filename,
},
}
}
pub fn path(&self) -> &VaultPath {
match self {
Self::Up { parent } => parent,
Self::Note { path, .. } => path,
Self::Directory { path, .. } => path,
Self::Attachment { path, .. } => path,
Self::CreateNote { path, .. } => path,
}
}
pub(crate) fn sort_key(&self, field: SortField) -> String {
match self {
Self::Up { .. } => String::new(),
Self::Note {
title, filename, ..
} => match field {
SortField::Title => title.to_lowercase(),
SortField::Name => filename.to_lowercase(),
},
Self::Directory { name, .. } => name.to_lowercase(),
Self::Attachment { filename, .. } => filename.to_lowercase(),
Self::CreateNote { filename, .. } => filename.to_lowercase(),
}
}
pub fn visual_height(&self) -> u16 {
match self {
Self::Note { journal_date, .. } => {
if journal_date.is_some() {
3
} else {
2
}
}
_ => 1,
}
}
pub fn to_list_item(&self, theme: &Theme, icons: &Icons) -> ListItem<'static> {
match self {
Self::Up { .. } => RichRow::new(icons.directory_up, "[UP] ..")
.glyph_style(Style::default().fg(theme.gray.to_ratatui()))
.title_style(Style::default().fg(theme.gray.to_ratatui()))
.into_list_item(theme),
Self::Note {
title,
filename,
journal_date,
..
} => {
let glyph = if journal_date.is_some() {
icons.journal
} else {
icons.note
};
let mut row = RichRow::new(glyph, title.clone()).filename(filename.clone());
if let Some(date) = journal_date {
row = row.secondary(
date.clone(),
Some(Style::default().fg(theme.color_journal_date.to_ratatui())),
);
}
row.into_list_item(theme)
}
Self::Directory { name, .. } => {
let dir_style = Style::default().fg(theme.color_directory.to_ratatui());
RichRow::new(icons.directory, name.clone())
.glyph_style(dir_style)
.title_style(dir_style)
.into_list_item(theme)
}
Self::Attachment { filename, .. } => {
let style = Style::default()
.add_modifier(Modifier::ITALIC)
.fg(theme.fg_secondary.to_ratatui());
RichRow::new(icons.attachment, filename.clone())
.glyph_style(style)
.title_style(style)
.into_list_item(theme)
}
Self::CreateNote { filename, .. } => {
let style = Style::default().fg(theme.accent.to_ratatui());
RichRow::new("+", format!("Create: {}", filename))
.glyph_style(style)
.title_style(style)
.into_list_item(theme)
}
}
}
}
impl crate::components::search_list::SearchRow for FileListEntry {
fn to_list_item(&self, theme: &Theme, icons: &Icons, _selected: bool) -> ListItem<'static> {
FileListEntry::to_list_item(self, theme, icons)
}
fn visual_height(&self) -> u16 {
FileListEntry::visual_height(self)
}
fn match_text(&self) -> Option<&str> {
match self {
Self::Note { filename, .. } | Self::CreateNote { filename, .. } => Some(filename),
Self::Directory { name, .. } => Some(name),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::components::search_list::SearchRow;
#[test]
fn directory_match_text_is_some_name() {
let dir = FileListEntry::Directory {
path: VaultPath::note_path_from("projects"),
name: "projects".to_string(),
};
assert_eq!(SearchRow::match_text(&dir), Some("projects"));
}
#[test]
fn up_match_text_is_none() {
let up = FileListEntry::Up {
parent: VaultPath::root(),
};
assert_eq!(SearchRow::match_text(&up), None);
}
#[test]
fn sort_field_setting_roundtrip() {
use crate::settings::SortFieldSetting;
assert_eq!(
SortFieldSetting::from(SortField::Name),
SortFieldSetting::Name
);
assert_eq!(
SortFieldSetting::from(SortField::Title),
SortFieldSetting::Title
);
assert_eq!(SortField::from(SortFieldSetting::Title), SortField::Title);
}
#[test]
fn sort_order_setting_roundtrip() {
use crate::settings::SortOrderSetting;
assert_eq!(
SortOrderSetting::from(SortOrder::Ascending),
SortOrderSetting::Ascending
);
assert_eq!(
SortOrderSetting::from(SortOrder::Descending),
SortOrderSetting::Descending
);
}
}