notedeck.rs (5830B)
1 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release 2 use notedeck_chrome::{setup::generate_native_options, Notedeck}; 3 4 use notedeck::{DataPath, DataPathType}; 5 use notedeck_columns::Damus; 6 use std::path::PathBuf; 7 use std::str::FromStr; 8 use tracing_subscriber::EnvFilter; 9 10 // Entry point for wasm 11 //#[cfg(target_arch = "wasm32")] 12 //use wasm_bindgen::prelude::*; 13 14 fn setup_logging(path: &DataPath) { 15 #[allow(unused_variables)] // need guard to live for lifetime of program 16 let (maybe_non_blocking, maybe_guard) = { 17 let log_path = path.path(DataPathType::Log); 18 // Setup logging to file 19 20 use tracing_appender::{ 21 non_blocking, 22 rolling::{RollingFileAppender, Rotation}, 23 }; 24 25 let file_appender = RollingFileAppender::new( 26 Rotation::DAILY, 27 log_path, 28 format!("notedeck-{}.log", env!("CARGO_PKG_VERSION")), 29 ); 30 31 let (non_blocking, _guard) = non_blocking(file_appender); 32 33 (Some(non_blocking), Some(_guard)) 34 }; 35 36 // Log to stdout (if you run with `RUST_LOG=debug`). 37 if let Some(non_blocking_writer) = maybe_non_blocking { 38 use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt}; 39 40 let console_layer = fmt::layer().with_target(true).with_writer(std::io::stdout); 41 42 // Create the file layer (writes to the file) 43 let file_layer = fmt::layer() 44 .with_ansi(false) 45 .with_writer(non_blocking_writer); 46 47 let env_filter = 48 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("notedeck=info")); 49 50 // Set up the subscriber to combine both layers 51 tracing_subscriber::registry() 52 .with(console_layer) 53 .with(file_layer) 54 .with(env_filter) 55 .init(); 56 } else { 57 tracing_subscriber::fmt() 58 .with_env_filter(EnvFilter::from_default_env()) 59 .init(); 60 } 61 } 62 63 // Desktop 64 #[cfg(not(target_arch = "wasm32"))] 65 #[tokio::main] 66 async fn main() { 67 let base_path = DataPath::default_base().unwrap_or(PathBuf::from_str(".").unwrap()); 68 let path = DataPath::new(&base_path); 69 70 setup_logging(&path); 71 72 let _res = eframe::run_native( 73 "Damus Notedeck", 74 generate_native_options(path), 75 Box::new(|cc| { 76 let args: Vec<String> = std::env::args().collect(); 77 let mut notedeck = Notedeck::new(&cc.egui_ctx, base_path, &args); 78 79 let damus = Damus::new(&mut notedeck.app_context(), &args); 80 notedeck.add_app(damus); 81 82 Ok(Box::new(notedeck)) 83 }), 84 ); 85 } 86 87 /* 88 * TODO: nostrdb not supported on web 89 * 90 #[cfg(target_arch = "wasm32")] 91 pub fn main() { 92 // Make sure panics are logged using `console.error`. 93 console_error_panic_hook::set_once(); 94 95 // Redirect tracing to console.log and friends: 96 tracing_wasm::set_as_global_default(); 97 98 wasm_bindgen_futures::spawn_local(async { 99 let web_options = eframe::WebOptions::default(); 100 eframe::start_web( 101 "the_canvas_id", // hardcode it 102 web_options, 103 Box::new(|cc| Box::new(Damus::new(cc, "."))), 104 ) 105 .await 106 .expect("failed to start eframe"); 107 }); 108 } 109 */ 110 111 #[cfg(test)] 112 mod tests { 113 use super::{Damus, Notedeck}; 114 use std::path::{Path, PathBuf}; 115 116 fn create_tmp_dir() -> PathBuf { 117 tempfile::TempDir::new() 118 .expect("tmp path") 119 .path() 120 .to_path_buf() 121 } 122 123 fn rmrf(path: impl AsRef<Path>) { 124 let _ = std::fs::remove_dir_all(path); 125 } 126 127 /// Ensure dbpath actually sets the dbpath correctly. 128 #[tokio::test] 129 async fn test_dbpath() { 130 let datapath = create_tmp_dir(); 131 let dbpath = create_tmp_dir(); 132 let args: Vec<String> = vec![ 133 "--testrunner", 134 "--datapath", 135 &datapath.to_str().unwrap(), 136 "--dbpath", 137 &dbpath.to_str().unwrap(), 138 ] 139 .iter() 140 .map(|s| s.to_string()) 141 .collect(); 142 143 let ctx = egui::Context::default(); 144 let _app = Notedeck::new(&ctx, &datapath, &args); 145 146 assert!(Path::new(&dbpath.join("data.mdb")).exists()); 147 assert!(Path::new(&dbpath.join("lock.mdb")).exists()); 148 assert!(!Path::new(&datapath.join("db")).exists()); 149 150 rmrf(datapath); 151 rmrf(dbpath); 152 } 153 154 #[tokio::test] 155 async fn test_column_args() { 156 let tmpdir = create_tmp_dir(); 157 let npub = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"; 158 let args: Vec<String> = vec![ 159 "--testrunner", 160 "--no-keystore", 161 "--pub", 162 npub, 163 "-c", 164 "notifications", 165 "-c", 166 "contacts", 167 ] 168 .iter() 169 .map(|s| s.to_string()) 170 .collect(); 171 172 let ctx = egui::Context::default(); 173 let mut notedeck = Notedeck::new(&ctx, &tmpdir, &args); 174 let mut app_ctx = notedeck.app_context(); 175 let app = Damus::new(&mut app_ctx, &args); 176 177 assert_eq!(app.columns(app_ctx.accounts).columns().len(), 2); 178 179 let tl1 = app 180 .columns(app_ctx.accounts) 181 .column(0) 182 .router() 183 .top() 184 .timeline_id(); 185 186 let tl2 = app 187 .columns(app_ctx.accounts) 188 .column(1) 189 .router() 190 .top() 191 .timeline_id(); 192 193 assert_eq!(tl1.is_some(), true); 194 assert_eq!(tl2.is_some(), true); 195 196 let timelines = app.columns(app_ctx.accounts).timelines(); 197 assert!(timelines[0].kind.is_notifications()); 198 assert!(timelines[1].kind.is_contacts()); 199 200 rmrf(tmpdir); 201 } 202 }