Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist columns #390

Merged
merged 2 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/account_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::cmp::Ordering;

use enostr::{FilledKeypair, FullKeypair, Keypair};
use nostrdb::Ndb;
use serde::{Deserialize, Serialize};

use crate::{
column::Columns,
Expand Down Expand Up @@ -32,7 +33,7 @@ pub enum AccountsRouteResponse {
AddAccount(AccountLoginResponse),
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub enum AccountsRoute {
Accounts,
AddAccount,
Expand Down
41 changes: 32 additions & 9 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
notecache::{CachedNote, NoteCache},
notes_holder::NotesHolderStorage,
profile::Profile,
storage::{Directory, FileKeyStorage, KeyStorageType},
storage::{self, Directory, FileKeyStorage, KeyStorageType},
subscriptions::{SubKind, Subscriptions},
support::Support,
thread::Thread,
Expand Down Expand Up @@ -727,12 +727,28 @@ impl Damus {
.map(|a| a.pubkey.bytes());
let ndb = Ndb::new(&dbpath, &config).expect("ndb");

let mut columns: Columns = Columns::new();
for col in parsed_args.columns {
if let Some(timeline) = col.into_timeline(&ndb, account) {
columns.add_new_timeline_column(timeline);
let mut columns = if parsed_args.columns.is_empty() {
if let Some(serializable_columns) = storage::load_columns() {
info!("Using columns from disk");
serializable_columns.into_columns(&ndb, account)
} else {
info!("Could not load columns from disk");
Columns::new()
}
}
} else {
info!(
"Using columns from command line arguments: {:?}",
parsed_args.columns
);
let mut columns: Columns = Columns::new();
for col in parsed_args.columns {
if let Some(timeline) = col.into_timeline(&ndb, account) {
columns.add_new_timeline_column(timeline);
}
}

columns
};

let debug = parsed_args.debug;

Expand Down Expand Up @@ -971,8 +987,8 @@ fn render_damus_mobile(ctx: &egui::Context, app: &mut Damus) {
//let routes = app.timelines[0].routes.clone();

main_panel(&ctx.style(), ui::is_narrow(ctx)).show(ctx, |ui| {
if !app.columns.columns().is_empty() {
nav::render_nav(0, app, ui);
if !app.columns.columns().is_empty() && nav::render_nav(0, app, ui) {
storage::save_columns(app.columns.as_serializable_columns());
}
});
}
Expand Down Expand Up @@ -1049,10 +1065,13 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
);
});

let mut columns_changed = false;
for col_index in 0..app.columns.num_columns() {
strip.cell(|ui| {
let rect = ui.available_rect_before_wrap();
nav::render_nav(col_index, app, ui);
if nav::render_nav(col_index, app, ui) {
columns_changed = true;
}

// vertical line
ui.painter().vline(
Expand All @@ -1064,6 +1083,10 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {

//strip.cell(|ui| timeline::timeline_view(ui, app, timeline_ind));
}

if columns_changed {
storage::save_columns(app.columns.as_serializable_columns());
}
});
}

Expand Down
7 changes: 1 addition & 6 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,18 +219,13 @@ impl Args {
i += 1;
}

if res.columns.is_empty() {
let ck = TimelineKind::contact_list(PubkeySource::DeckAuthor);
info!("No columns set, setting up defaults: {:?}", ck);
res.columns.push(ArgColumn::Timeline(ck));
}

res
}
}

/// A way to define columns from the commandline. Can be column kinds or
/// generic queries
#[derive(Debug)]
pub enum ArgColumn {
Timeline(TimelineKind),
Generic(Vec<Filter>),
Expand Down
88 changes: 86 additions & 2 deletions src/column.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::route::{Route, Router};
use crate::timeline::{Timeline, TimelineId};
use crate::timeline::{SerializableTimeline, Timeline, TimelineId, TimelineRoute};
use indexmap::IndexMap;
use nostrdb::Ndb;
use serde::{Deserialize, Deserializer, Serialize};
use std::iter::Iterator;
use std::sync::atomic::{AtomicU32, Ordering};
use tracing::warn;
use tracing::{error, warn};

#[derive(Clone)]
pub struct Column {
router: Router<Route>,
}
Expand All @@ -24,6 +27,28 @@ impl Column {
}
}

impl serde::Serialize for Column {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.router.routes().serialize(serializer)
}
}

impl<'de> Deserialize<'de> for Column {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let routes = Vec::<Route>::deserialize(deserializer)?;

Ok(Column {
router: Router::new(routes),
})
}
}

#[derive(Default)]
pub struct Columns {
/// Columns are simply routers into settings, timelines, etc
Expand Down Expand Up @@ -68,6 +93,10 @@ impl Columns {
UIDS.fetch_add(1, Ordering::Relaxed)
}

pub fn add_column_at(&mut self, column: Column, index: u32) {
self.columns.insert(index, column);
}

pub fn add_column(&mut self, column: Column) {
self.columns.insert(Self::get_new_id(), column);
}
Expand Down Expand Up @@ -194,4 +223,59 @@ impl Columns {
}
}
}

pub fn as_serializable_columns(&self) -> SerializableColumns {
SerializableColumns {
columns: self.columns.values().cloned().collect(),
timelines: self
.timelines
.values()
.map(|t| t.as_serializable_timeline())
.collect(),
}
}
}

#[derive(Serialize, Deserialize)]
pub struct SerializableColumns {
pub columns: Vec<Column>,
pub timelines: Vec<SerializableTimeline>,
}

impl SerializableColumns {
pub fn into_columns(self, ndb: &Ndb, deck_pubkey: Option<&[u8; 32]>) -> Columns {
let mut columns = Columns::default();

for column in self.columns {
let id = Columns::get_new_id();
let mut routes = Vec::new();
for route in column.router.routes() {
match route {
Route::Timeline(TimelineRoute::Timeline(timeline_id)) => {
if let Some(serializable_tl) =
self.timelines.iter().find(|tl| tl.id == *timeline_id)
{
let tl = serializable_tl.clone().into_timeline(ndb, deck_pubkey);
if let Some(tl) = tl {
routes.push(Route::Timeline(TimelineRoute::Timeline(tl.id)));
columns.timelines.insert(id, tl);
} else {
error!("Problem deserializing timeline {:?}", serializable_tl);
}
}
}
Route::Timeline(TimelineRoute::Thread(_thread)) => {
// TODO: open thread before pushing route
}
Route::Profile(_profile) => {
// TODO: open profile before pushing route
}
_ => routes.push(*route),
}
}
columns.add_column_at(Column::new(routes), id);
}

columns
}
}
7 changes: 6 additions & 1 deletion src/nav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use egui_nav::{Nav, NavAction, TitleBarResponse};
use nostrdb::{Ndb, Transaction};
use tracing::{error, info};

pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) -> bool {
let mut col_changed = false;
let col_id = app.columns.get_column_id_at_index(col);
// TODO(jb55): clean up this router_mut mess by using Router<R> in egui-nav directly
let routes = app
Expand Down Expand Up @@ -201,12 +202,14 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
pubkey.bytes(),
);
}
col_changed = true;
} else if let Some(NavAction::Navigated) = nav_response.action {
let cur_router = app.columns_mut().column_mut(col).router_mut();
cur_router.navigating = false;
if cur_router.is_replacing() {
cur_router.remove_previous_route();
}
col_changed = true;
}

if let Some(title_response) = nav_response.title_response {
Expand All @@ -220,6 +223,8 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
}
}
}

col_changed
}

fn unsubscribe_timeline(ndb: &Ndb, timeline: &Timeline) {
Expand Down
3 changes: 2 additions & 1 deletion src/route.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use enostr::{NoteId, Pubkey};
use nostrdb::Ndb;
use serde::{Deserialize, Serialize};
use std::fmt::{self};

use crate::{
Expand All @@ -10,7 +11,7 @@ use crate::{
};

/// App routing. These describe different places you can go inside Notedeck.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum Route {
Timeline(TimelineRoute),
Accounts(AccountsRoute),
Expand Down
60 changes: 60 additions & 0 deletions src/storage/columns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use tracing::{error, info};

use crate::column::SerializableColumns;

use super::{write_file, DataPaths, Directory};

static COLUMNS_FILE: &str = "columns.json";

pub fn save_columns(columns: SerializableColumns) {
let serialized_columns = match serde_json::to_string(&columns) {
Ok(s) => s,
Err(e) => {
error!("Could not serialize columns: {}", e);
return;
}
};

let data_path = match DataPaths::Setting.get_path() {
Ok(s) => s,
Err(e) => {
error!("Could not get data path: {}", e);
return;
}
};

if let Err(e) = write_file(&data_path, COLUMNS_FILE.to_string(), &serialized_columns) {
error!("Could not write columns to file {}: {}", COLUMNS_FILE, e);
} else {
info!("Successfully wrote columns to {}", COLUMNS_FILE);
}
}

pub fn load_columns() -> Option<SerializableColumns> {
let data_path = match DataPaths::Setting.get_path() {
Ok(s) => s,
Err(e) => {
error!("Could not get data path: {}", e);
return None;
}
};

let columns_string = match Directory::new(data_path).get_file(COLUMNS_FILE.to_owned()) {
Ok(s) => s,
Err(e) => {
error!("Could not read columns from file {}: {}", COLUMNS_FILE, e);
return None;
}
};

match serde_json::from_str::<SerializableColumns>(&columns_string) {
Ok(s) => {
info!("Successfully loaded columns from {}", COLUMNS_FILE);
Some(s)
}
Err(e) => {
error!("Could not deserialize columns: {}", e);
None
}
}
}
2 changes: 2 additions & 0 deletions src/storage/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod columns;
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod file_key_storage;
mod file_storage;

pub use columns::{load_columns, save_columns};
pub use file_key_storage::FileKeyStorage;
pub use file_storage::write_file;
pub use file_storage::DataPaths;
Expand Down
7 changes: 4 additions & 3 deletions src/timeline/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ use crate::timeline::Timeline;
use crate::ui::profile::preview::get_profile_displayname_string;
use enostr::{Filter, Pubkey};
use nostrdb::{Ndb, Transaction};
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use tracing::{error, warn};

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PubkeySource {
Explicit(Pubkey),
DeckAuthor,
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ListKind {
Contact(PubkeySource),
}
Expand All @@ -27,7 +28,7 @@ pub enum ListKind {
/// - filter
/// - ... etc
///
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TimelineKind {
List(ListKind),

Expand Down
Loading
Loading