feat: enhance diary navigation with LoadPrev action and pagination hints

This commit is contained in:
2026-05-07 00:35:03 +02:00
parent e1f2442e77
commit b3c243257d
5 changed files with 60 additions and 13 deletions

19
Cargo.lock generated
View File

@@ -1708,6 +1708,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
dependencies = [
"log",
"security-framework 2.11.1",
"security-framework 3.7.0",
"zeroize",
]
@@ -2887,7 +2889,7 @@ dependencies = [
"openssl-probe",
"rustls-pki-types",
"schannel",
"security-framework",
"security-framework 3.7.0",
]
[[package]]
@@ -2915,7 +2917,7 @@ dependencies = [
"rustls-native-certs",
"rustls-platform-verifier-android",
"rustls-webpki",
"security-framework",
"security-framework 3.7.0",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.61.2",
@@ -2975,6 +2977,19 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.11.1",
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework"
version = "3.7.0"

View File

@@ -6,7 +6,7 @@ edition = "2024"
[dependencies]
ratatui = "0.30.0"
keyring = "3"
keyring = { version = "3", features = ["apple-native"] }
directories = "6"
csv = "1"

View File

@@ -173,7 +173,7 @@ pub enum Action {
SetupSubmit,
InputChar(char), Backspace, FocusNext, FocusPrev,
LoginSubmit,
ScrollDown, ScrollUp, OpenHistory, LoadMore,
ScrollDown, ScrollUp, OpenHistory, LoadMore, LoadPrev,
DeleteInit, DeleteConfirm, DeleteCancel,
RatingUp, RatingDown, ReviewSubmit,
BulkParseFile, BulkImportAll, BulkCancel,
@@ -301,7 +301,7 @@ pub fn update(app: &mut App, action: Action) -> Vec<Command> {
m.bulk_import.stage = BulkImportStage::EnterPath;
}
}
_ => {}
Tab::AddReview | Tab::Settings => { m.tab = Tab::Diary; }
}
}
vec![]
@@ -435,8 +435,14 @@ pub fn update(app: &mut App, action: Action) -> Vec<Command> {
return vec![];
}
app.api_url = url.clone();
let cmds = if app.token.is_some() {
app.screen = Screen::Main(MainState::new(url.clone()));
vec![Command::SaveConfig(url), Command::LoadDiary { offset: 0 }]
} else {
app.screen = Screen::Login(LoginState::default());
return vec![Command::SaveConfig(url)];
vec![Command::SaveConfig(url)]
};
return cmds;
}
vec![]
}
@@ -515,6 +521,17 @@ pub fn update(app: &mut App, action: Action) -> Vec<Command> {
vec![]
}
Action::LoadPrev => {
if let Screen::Main(m) = &mut app.screen {
if m.diary.offset > 0 {
let prev = m.diary.offset.saturating_sub(20);
m.diary.offset = prev;
return vec![Command::LoadDiary { offset: prev }];
}
}
vec![]
}
Action::DiaryLoaded { entries, total } => {
app.loading = false;
if let Screen::Main(m) = &mut app.screen {
@@ -701,8 +718,12 @@ pub fn update(app: &mut App, action: Action) -> Vec<Command> {
Action::BulkCancel => {
if let Screen::Main(m) = &mut app.screen {
if m.bulk_import.stage == BulkImportStage::EnterPath {
m.tab = Tab::Diary;
} else {
m.bulk_import = BulkImportState::default();
}
}
vec![]
}

View File

@@ -246,6 +246,7 @@ fn key_to_action(app: &App, key: ratatui::crossterm::event::KeyEvent) -> Option<
KeyCode::Tab => Some(Action::TabNext),
KeyCode::BackTab => Some(Action::TabPrev),
KeyCode::Char('>') | KeyCode::Char('m') => Some(Action::LoadMore),
KeyCode::Char('<') | KeyCode::Char('b') => Some(Action::LoadPrev),
KeyCode::Char('1') => Some(Action::TabSelect(Tab::Diary)),
KeyCode::Char('2') => Some(Action::TabSelect(Tab::AddReview)),
KeyCode::Char('3') => Some(Action::TabSelect(Tab::BulkImport)),
@@ -279,6 +280,10 @@ fn key_to_action(app: &App, key: ratatui::crossterm::event::KeyEvent) -> Option<
KeyCode::Tab if !in_path => Some(Action::TabNext),
KeyCode::BackTab if !in_path => Some(Action::TabPrev),
KeyCode::Char('q') if !in_path => Some(Action::Quit),
KeyCode::Char('1') if !in_path => Some(Action::TabSelect(Tab::Diary)),
KeyCode::Char('2') if !in_path => Some(Action::TabSelect(Tab::AddReview)),
KeyCode::Char('3') if !in_path => Some(Action::TabSelect(Tab::BulkImport)),
KeyCode::Char('4') if !in_path => Some(Action::TabSelect(Tab::Settings)),
_ => None,
}
}
@@ -296,6 +301,10 @@ fn key_to_action(app: &App, key: ratatui::crossterm::event::KeyEvent) -> Option<
},
KeyCode::Esc => Some(Action::Escape),
KeyCode::Char('q') => Some(Action::Quit),
KeyCode::Char('1') if !on_url => Some(Action::TabSelect(Tab::Diary)),
KeyCode::Char('2') if !on_url => Some(Action::TabSelect(Tab::AddReview)),
KeyCode::Char('3') if !on_url => Some(Action::TabSelect(Tab::BulkImport)),
KeyCode::Char('4') if !on_url => Some(Action::TabSelect(Tab::Settings)),
_ => None,
}
}

View File

@@ -215,11 +215,13 @@ fn draw_diary(frame: &mut Frame, area: Rect, state: &DiaryState) {
.collect();
let can_load_more = (state.offset as u64 + state.entries.len() as u64) < state.total;
let list_title = if can_load_more {
format!(" Diary ({} entries) [m: load more] ", state.total)
} else {
format!(" Diary ({} entries) ", state.total)
};
let can_load_prev = state.offset > 0;
let page = state.offset / 20 + 1;
let total_pages = state.total.div_ceil(20).max(1);
let mut hints = format!(" Diary ({} entries, page {}/{}) ", state.total, page, total_pages);
if can_load_prev { hints.push_str("[b: prev] "); }
if can_load_more { hints.push_str("[m: next] "); }
let list_title = hints;
let mut list_state = ListState::default();
list_state.select(Some(state.selected));
let list = List::new(items).block(Block::default().title(list_title).borders(Borders::ALL));