Introducing Damus Notedeck: a nostr browser
This splits notedeck into: - notedeck - notedeck_chrome - notedeck_columns The `notedeck` crate is the library that `notedeck_chrome` and `notedeck_columns`, use. It contains common functionality related to notedeck apps such as the NoteCache, ImageCache, etc. The `notedeck_chrome` crate is the binary and ui chrome. It is responsible for managing themes, user accounts, signing, data paths, nostrdb, image caches etc. It will eventually have its own ui which has yet to be determined. For now it just manages the browser data, which is passed to apps via a new struct called `AppContext`. `notedeck_columns` is our columns app, with less responsibility now that more things are handled by `notedeck_chrome` There is still much work left to do before this is a proper browser: - process isolation - sandboxing - etc This is the beginning of a new era! We're just getting started. Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -1,37 +1,22 @@
|
||||
use crate::filter::FilterState;
|
||||
use notedeck::FilterState;
|
||||
|
||||
use crate::timeline::{PubkeySource, Timeline, TimelineKind};
|
||||
use enostr::{Filter, Keypair, Pubkey, SecretKey};
|
||||
use enostr::{Filter, Pubkey};
|
||||
use nostrdb::Ndb;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
pub struct Args {
|
||||
pub struct ColumnsArgs {
|
||||
pub columns: Vec<ArgColumn>,
|
||||
pub relays: Vec<String>,
|
||||
pub is_mobile: Option<bool>,
|
||||
pub keys: Vec<Keypair>,
|
||||
pub since_optimize: bool,
|
||||
pub light: bool,
|
||||
pub debug: bool,
|
||||
pub textmode: bool,
|
||||
pub use_keystore: bool,
|
||||
pub dbpath: Option<String>,
|
||||
pub datapath: Option<String>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
impl ColumnsArgs {
|
||||
pub fn parse(args: &[String]) -> Self {
|
||||
let mut res = Args {
|
||||
let mut res = Self {
|
||||
columns: vec![],
|
||||
relays: vec![],
|
||||
is_mobile: None,
|
||||
keys: vec![],
|
||||
light: false,
|
||||
since_optimize: true,
|
||||
debug: false,
|
||||
textmode: false,
|
||||
use_keystore: true,
|
||||
dbpath: None,
|
||||
datapath: None,
|
||||
};
|
||||
|
||||
let mut i = 0;
|
||||
@@ -39,50 +24,8 @@ impl Args {
|
||||
while i < len {
|
||||
let arg = &args[i];
|
||||
|
||||
if arg == "--mobile" {
|
||||
res.is_mobile = Some(true);
|
||||
} else if arg == "--light" {
|
||||
res.light = true;
|
||||
} else if arg == "--dark" {
|
||||
res.light = false;
|
||||
} else if arg == "--debug" {
|
||||
res.debug = true;
|
||||
} else if arg == "--textmode" {
|
||||
if arg == "--textmode" {
|
||||
res.textmode = true;
|
||||
} else if arg == "--pub" || arg == "--npub" {
|
||||
i += 1;
|
||||
let pubstr = if let Some(next_arg) = args.get(i) {
|
||||
next_arg
|
||||
} else {
|
||||
error!("sec argument missing?");
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(pk) = Pubkey::parse(pubstr) {
|
||||
res.keys.push(Keypair::only_pubkey(pk));
|
||||
} else {
|
||||
error!(
|
||||
"failed to parse {} argument. Make sure to use hex or npub.",
|
||||
arg
|
||||
);
|
||||
}
|
||||
} else if arg == "--sec" || arg == "--nsec" {
|
||||
i += 1;
|
||||
let secstr = if let Some(next_arg) = args.get(i) {
|
||||
next_arg
|
||||
} else {
|
||||
error!("sec argument missing?");
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Ok(sec) = SecretKey::parse(secstr) {
|
||||
res.keys.push(Keypair::from_secret(sec));
|
||||
} else {
|
||||
error!(
|
||||
"failed to parse {} argument. Make sure to use hex or nsec.",
|
||||
arg
|
||||
);
|
||||
}
|
||||
} else if arg == "--no-since-optimize" {
|
||||
res.since_optimize = false;
|
||||
} else if arg == "--filter" {
|
||||
@@ -99,33 +42,6 @@ impl Args {
|
||||
} else {
|
||||
error!("failed to parse filter '{}'", filter);
|
||||
}
|
||||
} else if arg == "--dbpath" {
|
||||
i += 1;
|
||||
let path = if let Some(next_arg) = args.get(i) {
|
||||
next_arg
|
||||
} else {
|
||||
error!("dbpath argument missing?");
|
||||
continue;
|
||||
};
|
||||
res.dbpath = Some(path.clone());
|
||||
} else if arg == "--datapath" {
|
||||
i += 1;
|
||||
let path = if let Some(next_arg) = args.get(i) {
|
||||
next_arg
|
||||
} else {
|
||||
error!("datapath argument missing?");
|
||||
continue;
|
||||
};
|
||||
res.datapath = Some(path.clone());
|
||||
} else if arg == "-r" || arg == "--relay" {
|
||||
i += 1;
|
||||
let relay = if let Some(next_arg) = args.get(i) {
|
||||
next_arg
|
||||
} else {
|
||||
error!("relay argument missing?");
|
||||
continue;
|
||||
};
|
||||
res.relays.push(relay.clone());
|
||||
} else if arg == "--column" || arg == "-c" {
|
||||
i += 1;
|
||||
let column_name = if let Some(next_arg) = args.get(i) {
|
||||
@@ -212,8 +128,6 @@ impl Args {
|
||||
} else {
|
||||
error!("failed to parse filter in '{}'", filter_file);
|
||||
}
|
||||
} else if arg == "--no-keystore" {
|
||||
res.use_keystore = false;
|
||||
}
|
||||
|
||||
i += 1;
|
||||
@@ -242,82 +156,3 @@ impl ArgColumn {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::app::Damus;
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn create_tmp_dir() -> PathBuf {
|
||||
tempfile::TempDir::new()
|
||||
.expect("tmp path")
|
||||
.path()
|
||||
.to_path_buf()
|
||||
}
|
||||
|
||||
fn rmrf(path: impl AsRef<Path>) {
|
||||
let _ = std::fs::remove_dir_all(path);
|
||||
}
|
||||
|
||||
/// Ensure dbpath actually sets the dbpath correctly.
|
||||
#[tokio::test]
|
||||
async fn test_dbpath() {
|
||||
let datapath = create_tmp_dir();
|
||||
let dbpath = create_tmp_dir();
|
||||
let args = vec![
|
||||
"--datapath",
|
||||
&datapath.to_str().unwrap(),
|
||||
"--dbpath",
|
||||
&dbpath.to_str().unwrap(),
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
let ctx = egui::Context::default();
|
||||
let _app = Damus::new(&ctx, &datapath, args);
|
||||
|
||||
assert!(Path::new(&dbpath.join("data.mdb")).exists());
|
||||
assert!(Path::new(&dbpath.join("lock.mdb")).exists());
|
||||
assert!(!Path::new(&datapath.join("db")).exists());
|
||||
|
||||
rmrf(datapath);
|
||||
rmrf(dbpath);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_column_args() {
|
||||
let tmpdir = create_tmp_dir();
|
||||
let npub = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s";
|
||||
let args = vec![
|
||||
"--no-keystore",
|
||||
"--pub",
|
||||
npub,
|
||||
"-c",
|
||||
"notifications",
|
||||
"-c",
|
||||
"contacts",
|
||||
]
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
let ctx = egui::Context::default();
|
||||
let app = Damus::new(&ctx, &tmpdir, args);
|
||||
|
||||
assert_eq!(app.columns().columns().len(), 2);
|
||||
|
||||
let tl1 = app.columns().column(0).router().top().timeline_id();
|
||||
let tl2 = app.columns().column(1).router().top().timeline_id();
|
||||
|
||||
assert_eq!(tl1.is_some(), true);
|
||||
assert_eq!(tl2.is_some(), true);
|
||||
|
||||
let timelines = app.columns().timelines();
|
||||
assert!(timelines[0].kind.is_notifications());
|
||||
assert!(timelines[1].kind.is_contacts());
|
||||
|
||||
rmrf(tmpdir);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user