Handle idle time between fetch results #212

Merged
wojtek merged 18 commits from 211---handle-idle-time-between-fetch-results into main 2024-09-08 23:23:53 +02:00
11 changed files with 54 additions and 42 deletions
Showing only changes of commit 56bf99f985 - Show all commits

View File

@ -94,7 +94,6 @@ fn with<Database: IDatabase + 'static, Library: ILibrary + 'static>(
let app = App::new(music_hoard, musicbrainz, app_sender); let app = App::new(music_hoard, musicbrainz, app_sender);
let ui = Ui; let ui = Ui;
// Run the TUI application. // Run the TUI application.
Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui"); Tui::run(terminal, app, ui, handler, listener).expect("failed to run tui");
} }

View File

@ -13,7 +13,7 @@ use crate::tui::{
app::{ app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
selection::{Delta, ListSelection}, selection::{Delta, ListSelection},
AppMatchesInfo, AppPublic, AppState, IAppInteractBrowse, AppMatchesInfo, AppPublic, AppState, IAppBase, IAppInteractBrowse,
}, },
event::{Event, EventSender}, event::{Event, EventSender},
lib::interface::musicbrainz::{self, IMusicBrainz}, lib::interface::musicbrainz::{self, IMusicBrainz},
@ -183,6 +183,10 @@ impl IAppInteractBrowse for AppMachine<AppBrowse> {
AppMachine::app_fetch_new(self.inner, fetch_rx) AppMachine::app_fetch_new(self.inner, fetch_rx)
} }
}
impl IAppBase for AppMachine<AppBrowse> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -1,6 +1,6 @@
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppState, IAppInteractCritical, AppPublic, AppState, IAppBase,
}; };
pub struct AppCritical { pub struct AppCritical {
@ -33,7 +33,7 @@ impl<'a> From<&'a mut AppMachine<AppCritical>> for AppPublic<'a> {
} }
} }
impl IAppInteractCritical for AppMachine<AppCritical> { impl IAppBase for AppMachine<AppCritical> {
type APP = App; type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {

View File

@ -1,6 +1,6 @@
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppState, IAppInteractError, AppPublic, AppState, IAppBase, IAppInteractError,
}; };
pub struct AppError { pub struct AppError {
@ -39,6 +39,10 @@ impl IAppInteractError for AppMachine<AppError> {
fn dismiss_error(self) -> Self::APP { fn dismiss_error(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppError> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -3,7 +3,7 @@ use std::sync::mpsc::{self, TryRecvError};
use crate::tui::{ use crate::tui::{
app::{ app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppMatchesInfo, AppPublic, AppState, IAppEventFetch, IAppInteractFetch, AppMatchesInfo, AppPublic, AppState, IAppBase, IAppEventFetch, IAppInteractFetch,
}, },
lib::interface::musicbrainz::Error as MbError, lib::interface::musicbrainz::Error as MbError,
}; };
@ -85,6 +85,10 @@ impl IAppInteractFetch for AppMachine<AppFetch> {
fn abort(self) -> Self::APP { fn abort(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppFetch> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -1,6 +1,6 @@
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppPublic, AppState, IAppInteractInfo, AppPublic, AppState, IAppBase, IAppInteractInfo,
}; };
pub struct AppInfo; pub struct AppInfo;
@ -35,6 +35,10 @@ impl IAppInteractInfo for AppMachine<AppInfo> {
fn hide_info_overlay(self) -> Self::APP { fn hide_info_overlay(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppInfo> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -3,7 +3,7 @@ use std::cmp;
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
AppAlbumMatches, AppArtistMatches, AppMatchesInfo, AppPublic, AppPublicMatches, AppState, AppAlbumMatches, AppArtistMatches, AppMatchesInfo, AppPublic, AppPublicMatches, AppState,
IAppInteractMatches, MatchOption, WidgetState, IAppBase, IAppInteractMatches, MatchOption, WidgetState,
}; };
use super::fetch::AppFetch; use super::fetch::AppFetch;
@ -122,6 +122,10 @@ impl IAppInteractMatches for AppMachine<AppMatches> {
fn abort(self) -> Self::APP { fn abort(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppMatches> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -1,7 +1,7 @@
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
selection::KeySelection, selection::KeySelection,
AppPublic, AppState, IAppInteractReload, AppPublic, AppState, IAppBase, IAppInteractReload,
}; };
pub struct AppReload; pub struct AppReload;
@ -53,6 +53,10 @@ impl IAppInteractReload for AppMachine<AppReload> {
fn hide_reload_menu(self) -> Self::APP { fn hide_reload_menu(self) -> Self::APP {
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppReload> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -6,7 +6,7 @@ use musichoard::collection::{album::Album, artist::Artist, track::Track};
use crate::tui::app::{ use crate::tui::app::{
machine::{App, AppInner, AppMachine}, machine::{App, AppInner, AppMachine},
selection::{ListSelection, SelectionState}, selection::{ListSelection, SelectionState},
AppPublic, AppState, Category, IAppInteractSearch, AppPublic, AppState, Category, IAppBase, IAppInteractSearch,
}; };
// Unlikely that this covers all possible strings, but it should at least cover strings // Unlikely that this covers all possible strings, but it should at least cover strings
@ -98,6 +98,10 @@ impl IAppInteractSearch for AppMachine<AppSearch> {
self.inner.selection.select_by_list(self.state.orig); self.inner.selection.select_by_list(self.state.orig);
AppMachine::browse(self.inner).into() AppMachine::browse(self.inner).into()
} }
}
impl IAppBase for AppMachine<AppSearch> {
type APP = App;
fn no_op(self) -> Self::APP { fn no_op(self) -> Self::APP {
self.into() self.into()

View File

@ -20,14 +20,14 @@ pub enum AppState<BS, IS, RS, SS, FS, MS, ES, CS> {
} }
pub trait IAppInteract { pub trait IAppInteract {
type BS: IAppInteractBrowse<APP = Self>; type BS: IAppBase<APP = Self> + IAppInteractBrowse<APP = Self>;
type IS: IAppInteractInfo<APP = Self>; type IS: IAppBase<APP = Self> + IAppInteractInfo<APP = Self>;
type RS: IAppInteractReload<APP = Self>; type RS: IAppBase<APP = Self> + IAppInteractReload<APP = Self>;
type SS: IAppInteractSearch<APP = Self>; type SS: IAppBase<APP = Self> + IAppInteractSearch<APP = Self>;
type FS: IAppInteractFetch<APP = Self> + IAppEventFetch<APP = Self>; type FS: IAppBase<APP = Self> + IAppInteractFetch<APP = Self> + IAppEventFetch<APP = Self>;
type MS: IAppInteractMatches<APP = Self>; type MS: IAppBase<APP = Self> + IAppInteractMatches<APP = Self>;
type ES: IAppInteractError<APP = Self>; type ES: IAppBase<APP = Self> + IAppInteractError<APP = Self>;
type CS: IAppInteractCritical<APP = Self>; type CS: IAppBase<APP = Self>;
fn is_running(&self) -> bool; fn is_running(&self) -> bool;
fn force_quit(self) -> Self; fn force_quit(self) -> Self;
@ -38,6 +38,12 @@ pub trait IAppInteract {
) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>; ) -> AppState<Self::BS, Self::IS, Self::RS, Self::SS, Self::FS, Self::MS, Self::ES, Self::CS>;
} }
pub trait IAppBase {
type APP: IAppInteract;
fn no_op(self) -> Self::APP;
}
pub trait IAppInteractBrowse { pub trait IAppInteractBrowse {
type APP: IAppInteract; type APP: IAppInteract;
@ -55,16 +61,12 @@ pub trait IAppInteractBrowse {
fn begin_search(self) -> Self::APP; fn begin_search(self) -> Self::APP;
fn fetch_musicbrainz(self) -> Self::APP; fn fetch_musicbrainz(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractInfo { pub trait IAppInteractInfo {
type APP: IAppInteract; type APP: IAppInteract;
fn hide_info_overlay(self) -> Self::APP; fn hide_info_overlay(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractReload { pub trait IAppInteractReload {
@ -73,8 +75,6 @@ pub trait IAppInteractReload {
fn reload_library(self) -> Self::APP; fn reload_library(self) -> Self::APP;
fn reload_database(self) -> Self::APP; fn reload_database(self) -> Self::APP;
fn hide_reload_menu(self) -> Self::APP; fn hide_reload_menu(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractSearch { pub trait IAppInteractSearch {
@ -85,16 +85,12 @@ pub trait IAppInteractSearch {
fn step_back(self) -> Self::APP; fn step_back(self) -> Self::APP;
fn finish_search(self) -> Self::APP; fn finish_search(self) -> Self::APP;
fn cancel_search(self) -> Self::APP; fn cancel_search(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractFetch { pub trait IAppInteractFetch {
type APP: IAppInteract; type APP: IAppInteract;
fn abort(self) -> Self::APP; fn abort(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppEventFetch { pub trait IAppEventFetch {
@ -111,22 +107,12 @@ pub trait IAppInteractMatches {
fn select(self) -> Self::APP; fn select(self) -> Self::APP;
fn abort(self) -> Self::APP; fn abort(self) -> Self::APP;
fn no_op(self) -> Self::APP;
} }
pub trait IAppInteractError { pub trait IAppInteractError {
type APP: IAppInteract; type APP: IAppInteract;
fn dismiss_error(self) -> Self::APP; fn dismiss_error(self) -> Self::APP;
fn no_op(self) -> Self::APP;
}
pub trait IAppInteractCritical {
type APP: IAppInteract;
fn no_op(self) -> Self::APP;
} }
// It would be preferable to have a getter for each field separately. However, the selection field // It would be preferable to have a getter for each field separately. However, the selection field

View File

@ -5,14 +5,13 @@ use mockall::automock;
use crate::tui::{ use crate::tui::{
app::{ app::{
AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractCritical, IAppInteractError, AppState, Delta, IAppInteract, IAppInteractBrowse, IAppInteractError, IAppInteractFetch,
IAppInteractFetch, IAppInteractInfo, IAppInteractMatches, IAppInteractReload, IAppInteractInfo, IAppInteractMatches, IAppInteractReload, IAppInteractSearch,
IAppInteractSearch,
}, },
event::{Event, EventError, EventReceiver}, event::{Event, EventError, EventReceiver},
}; };
use super::app::IAppEventFetch; use super::app::{IAppBase, IAppEventFetch};
#[cfg_attr(test, automock)] #[cfg_attr(test, automock)]
pub trait IEventHandler<APP: IAppInteract> { pub trait IEventHandler<APP: IAppInteract> {