Flexible routing

Another massive refactor to change the way routing works. Now any
column can route anywhere.

Also things are generally just much better and more modular via the
new struct split borrowing technique.

I didn't even try to split this into smaller commits for my sanity.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin
2024-09-11 19:43:41 -07:00
parent b4a8cddc48
commit 36c0971fd9
27 changed files with 973 additions and 963 deletions

View File

@@ -1,51 +1,107 @@
use egui::RichText;
use enostr::NoteId;
use std::fmt::{self};
use strum_macros::Display;
use crate::ui::{
account_login_view::AccountLoginResponse, account_management::AccountManagementViewResponse,
use crate::{
account_manager::AccountsRoute,
timeline::{TimelineId, TimelineRoute},
};
/// App routing. These describe different places you can go inside Notedeck.
#[derive(Clone, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum Route {
Timeline(String),
Thread(NoteId),
Reply(NoteId),
Timeline(TimelineRoute),
Accounts(AccountsRoute),
Relays,
}
#[derive(Clone, Debug, Default, Display)]
pub enum ManageAccountRoute {
#[default]
AccountManagement,
AddAccount,
impl Route {
pub fn timeline(timeline_id: TimelineId) -> Self {
Route::Timeline(TimelineRoute::Timeline(timeline_id))
}
pub fn timeline_id(&self) -> Option<&TimelineId> {
if let Route::Timeline(TimelineRoute::Timeline(tid)) = self {
Some(tid)
} else {
None
}
}
pub fn thread(thread_root: NoteId) -> Self {
Route::Timeline(TimelineRoute::Thread(thread_root))
}
pub fn reply(replying_to: NoteId) -> Self {
Route::Timeline(TimelineRoute::Reply(replying_to))
}
pub fn accounts() -> Self {
Route::Accounts(AccountsRoute::Accounts)
}
pub fn add_account() -> Self {
Route::Accounts(AccountsRoute::AddAccount)
}
}
// TODO: add this to egui-nav so we don't have to deal with returning
// and navigating headaches
#[derive(Clone)]
pub struct Router<R: Clone> {
routes: Vec<R>,
pub returning: bool,
pub navigating: bool,
}
impl<R: Clone> Router<R> {
pub fn new(routes: Vec<R>) -> Self {
if routes.is_empty() {
panic!("routes can't be empty")
}
let returning = false;
let navigating = false;
Router {
routes,
returning,
navigating,
}
}
pub fn route_to(&mut self, route: R) {
self.routes.push(route);
}
pub fn go_back(&mut self) -> Option<R> {
if self.routes.len() == 1 {
return None;
}
self.routes.pop()
}
pub fn top(&self) -> &R {
self.routes.last().expect("routes can't be empty")
}
pub fn routes(&self) -> &Vec<R> {
&self.routes
}
}
impl fmt::Display for Route {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Route::Timeline(name) => write!(f, "{}", name),
Route::Thread(_id) => write!(f, "Thread"),
Route::Reply(_id) => write!(f, "Reply"),
Route::Timeline(tlr) => match tlr {
TimelineRoute::Timeline(name) => write!(f, "{}", name),
TimelineRoute::Thread(_id) => write!(f, "Thread"),
TimelineRoute::Reply(_id) => write!(f, "Reply"),
},
Route::Relays => write!(f, "Relays"),
Route::Accounts(amr) => match amr {
AccountsRoute::Accounts => write!(f, "Accounts"),
AccountsRoute::AddAccount => write!(f, "Add Account"),
},
}
}
}
impl Route {
pub fn title(&self) -> RichText {
match self {
Route::Thread(_) => RichText::new("Thread"),
Route::Reply(_) => RichText::new("Reply"),
Route::Relays => RichText::new("Relays"),
Route::Timeline(_) => RichText::new("Timeline"),
}
}
}
pub enum ManageAcountRouteResponse {
AccountManagement(AccountManagementViewResponse),
AddAccount(AccountLoginResponse),
}