initial column storage
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use enostr::{FilledKeypair, FullKeypair, Keypair};
|
use enostr::{FilledKeypair, FullKeypair, Keypair};
|
||||||
use nostrdb::Ndb;
|
use nostrdb::Ndb;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
column::Columns,
|
column::Columns,
|
||||||
@@ -32,7 +33,7 @@ pub enum AccountsRouteResponse {
|
|||||||
AddAccount(AccountLoginResponse),
|
AddAccount(AccountLoginResponse),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub enum AccountsRoute {
|
pub enum AccountsRoute {
|
||||||
Accounts,
|
Accounts,
|
||||||
AddAccount,
|
AddAccount,
|
||||||
|
|||||||
41
src/app.rs
41
src/app.rs
@@ -15,7 +15,7 @@ use crate::{
|
|||||||
notecache::{CachedNote, NoteCache},
|
notecache::{CachedNote, NoteCache},
|
||||||
notes_holder::NotesHolderStorage,
|
notes_holder::NotesHolderStorage,
|
||||||
profile::Profile,
|
profile::Profile,
|
||||||
storage::{Directory, FileKeyStorage, KeyStorageType},
|
storage::{self, Directory, FileKeyStorage, KeyStorageType},
|
||||||
subscriptions::{SubKind, Subscriptions},
|
subscriptions::{SubKind, Subscriptions},
|
||||||
support::Support,
|
support::Support,
|
||||||
thread::Thread,
|
thread::Thread,
|
||||||
@@ -727,12 +727,28 @@ impl Damus {
|
|||||||
.map(|a| a.pubkey.bytes());
|
.map(|a| a.pubkey.bytes());
|
||||||
let ndb = Ndb::new(&dbpath, &config).expect("ndb");
|
let ndb = Ndb::new(&dbpath, &config).expect("ndb");
|
||||||
|
|
||||||
let mut columns: Columns = Columns::new();
|
let mut columns = if parsed_args.columns.is_empty() {
|
||||||
for col in parsed_args.columns {
|
if let Some(serializable_columns) = storage::load_columns() {
|
||||||
if let Some(timeline) = col.into_timeline(&ndb, account) {
|
info!("Using columns from disk");
|
||||||
columns.add_new_timeline_column(timeline);
|
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;
|
let debug = parsed_args.debug;
|
||||||
|
|
||||||
@@ -971,8 +987,8 @@ fn render_damus_mobile(ctx: &egui::Context, app: &mut Damus) {
|
|||||||
//let routes = app.timelines[0].routes.clone();
|
//let routes = app.timelines[0].routes.clone();
|
||||||
|
|
||||||
main_panel(&ctx.style(), ui::is_narrow(ctx)).show(ctx, |ui| {
|
main_panel(&ctx.style(), ui::is_narrow(ctx)).show(ctx, |ui| {
|
||||||
if !app.columns.columns().is_empty() {
|
if !app.columns.columns().is_empty() && nav::render_nav(0, app, ui) {
|
||||||
nav::render_nav(0, app, ui);
|
storage::save_columns(app.columns.as_serializable_columns());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -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() {
|
for col_index in 0..app.columns.num_columns() {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
let rect = ui.available_rect_before_wrap();
|
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
|
// vertical line
|
||||||
ui.painter().vline(
|
ui.painter().vline(
|
||||||
@@ -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));
|
//strip.cell(|ui| timeline::timeline_view(ui, app, timeline_ind));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if columns_changed {
|
||||||
|
storage::save_columns(app.columns.as_serializable_columns());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,18 +219,13 @@ impl Args {
|
|||||||
i += 1;
|
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
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A way to define columns from the commandline. Can be column kinds or
|
/// A way to define columns from the commandline. Can be column kinds or
|
||||||
/// generic queries
|
/// generic queries
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ArgColumn {
|
pub enum ArgColumn {
|
||||||
Timeline(TimelineKind),
|
Timeline(TimelineKind),
|
||||||
Generic(Vec<Filter>),
|
Generic(Vec<Filter>),
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use crate::route::{Route, Router};
|
use crate::route::{Route, Router};
|
||||||
use crate::timeline::{Timeline, TimelineId};
|
use crate::timeline::{SerializableTimeline, Timeline, TimelineId, TimelineRoute};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use nostrdb::Ndb;
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use std::iter::Iterator;
|
use std::iter::Iterator;
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use tracing::warn;
|
use tracing::{error, warn};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Column {
|
pub struct Column {
|
||||||
router: Router<Route>,
|
router: Router<Route>,
|
||||||
}
|
}
|
||||||
@@ -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)]
|
#[derive(Default)]
|
||||||
pub struct Columns {
|
pub struct Columns {
|
||||||
/// Columns are simply routers into settings, timelines, etc
|
/// Columns are simply routers into settings, timelines, etc
|
||||||
@@ -68,6 +93,10 @@ impl Columns {
|
|||||||
UIDS.fetch_add(1, Ordering::Relaxed)
|
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) {
|
pub fn add_column(&mut self, column: Column) {
|
||||||
self.columns.insert(Self::get_new_id(), column);
|
self.columns.insert(Self::get_new_id(), column);
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ use egui_nav::{Nav, NavAction, TitleBarResponse};
|
|||||||
use nostrdb::{Ndb, Transaction};
|
use nostrdb::{Ndb, Transaction};
|
||||||
use tracing::{error, info};
|
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);
|
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
|
// TODO(jb55): clean up this router_mut mess by using Router<R> in egui-nav directly
|
||||||
let routes = app
|
let routes = app
|
||||||
@@ -201,12 +202,14 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
|
|||||||
pubkey.bytes(),
|
pubkey.bytes(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
col_changed = true;
|
||||||
} else if let Some(NavAction::Navigated) = nav_response.action {
|
} else if let Some(NavAction::Navigated) = nav_response.action {
|
||||||
let cur_router = app.columns_mut().column_mut(col).router_mut();
|
let cur_router = app.columns_mut().column_mut(col).router_mut();
|
||||||
cur_router.navigating = false;
|
cur_router.navigating = false;
|
||||||
if cur_router.is_replacing() {
|
if cur_router.is_replacing() {
|
||||||
cur_router.remove_previous_route();
|
cur_router.remove_previous_route();
|
||||||
}
|
}
|
||||||
|
col_changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(title_response) = nav_response.title_response {
|
if let Some(title_response) = nav_response.title_response {
|
||||||
@@ -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) {
|
fn unsubscribe_timeline(ndb: &Ndb, timeline: &Timeline) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use enostr::{NoteId, Pubkey};
|
use enostr::{NoteId, Pubkey};
|
||||||
use nostrdb::Ndb;
|
use nostrdb::Ndb;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::{self};
|
use std::fmt::{self};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -10,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// App routing. These describe different places you can go inside Notedeck.
|
/// 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 {
|
pub enum Route {
|
||||||
Timeline(TimelineRoute),
|
Timeline(TimelineRoute),
|
||||||
Accounts(AccountsRoute),
|
Accounts(AccountsRoute),
|
||||||
|
|||||||
60
src/storage/columns.rs
Normal file
60
src/storage/columns.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
|
mod columns;
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
mod file_key_storage;
|
mod file_key_storage;
|
||||||
mod file_storage;
|
mod file_storage;
|
||||||
|
|
||||||
|
pub use columns::{load_columns, save_columns};
|
||||||
pub use file_key_storage::FileKeyStorage;
|
pub use file_key_storage::FileKeyStorage;
|
||||||
pub use file_storage::write_file;
|
pub use file_storage::write_file;
|
||||||
pub use file_storage::DataPaths;
|
pub use file_storage::DataPaths;
|
||||||
|
|||||||
@@ -5,16 +5,17 @@ use crate::timeline::Timeline;
|
|||||||
use crate::ui::profile::preview::get_profile_displayname_string;
|
use crate::ui::profile::preview::get_profile_displayname_string;
|
||||||
use enostr::{Filter, Pubkey};
|
use enostr::{Filter, Pubkey};
|
||||||
use nostrdb::{Ndb, Transaction};
|
use nostrdb::{Ndb, Transaction};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum PubkeySource {
|
pub enum PubkeySource {
|
||||||
Explicit(Pubkey),
|
Explicit(Pubkey),
|
||||||
DeckAuthor,
|
DeckAuthor,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ListKind {
|
pub enum ListKind {
|
||||||
Contact(PubkeySource),
|
Contact(PubkeySource),
|
||||||
}
|
}
|
||||||
@@ -27,7 +28,7 @@ pub enum ListKind {
|
|||||||
/// - filter
|
/// - filter
|
||||||
/// - ... etc
|
/// - ... etc
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum TimelineKind {
|
pub enum TimelineKind {
|
||||||
List(ListKind),
|
List(ListKind),
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|||||||
|
|
||||||
use egui_virtual_list::VirtualList;
|
use egui_virtual_list::VirtualList;
|
||||||
use nostrdb::{Ndb, Note, Subscription, Transaction};
|
use nostrdb::{Ndb, Note, Subscription, Transaction};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -21,7 +22,7 @@ pub mod route;
|
|||||||
pub use kind::{PubkeySource, TimelineKind};
|
pub use kind::{PubkeySource, TimelineKind};
|
||||||
pub use route::TimelineRoute;
|
pub use route::TimelineRoute;
|
||||||
|
|
||||||
#[derive(Debug, Hash, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Hash, Copy, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct TimelineId(u32);
|
pub struct TimelineId(u32);
|
||||||
|
|
||||||
impl TimelineId {
|
impl TimelineId {
|
||||||
@@ -177,6 +178,18 @@ pub struct Timeline {
|
|||||||
pub subscription: Option<Subscription>,
|
pub subscription: Option<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SerializableTimeline {
|
||||||
|
pub id: TimelineId,
|
||||||
|
pub kind: TimelineKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializableTimeline {
|
||||||
|
pub fn into_timeline(self, ndb: &Ndb, deck_user_pubkey: Option<&[u8; 32]>) -> Option<Timeline> {
|
||||||
|
self.kind.into_timeline(ndb, deck_user_pubkey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Timeline {
|
impl Timeline {
|
||||||
/// Create a timeline from a contact list
|
/// Create a timeline from a contact list
|
||||||
pub fn contact_list(contact_list: &Note, pk_src: PubkeySource) -> Result<Self> {
|
pub fn contact_list(contact_list: &Note, pk_src: PubkeySource) -> Result<Self> {
|
||||||
@@ -312,6 +325,13 @@ impl Timeline {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_serializable_timeline(&self) -> SerializableTimeline {
|
||||||
|
SerializableTimeline {
|
||||||
|
id: self.id,
|
||||||
|
kind: self.kind.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MergeKind {
|
pub enum MergeKind {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use crate::{
|
|||||||
use enostr::{NoteId, Pubkey, RelayPool};
|
use enostr::{NoteId, Pubkey, RelayPool};
|
||||||
use nostrdb::{Ndb, Transaction};
|
use nostrdb::{Ndb, Transaction};
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
#[derive(Debug, Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum TimelineRoute {
|
pub enum TimelineRoute {
|
||||||
Timeline(TimelineId),
|
Timeline(TimelineId),
|
||||||
Thread(NoteId),
|
Thread(NoteId),
|
||||||
|
|||||||
Reference in New Issue
Block a user