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,38 +1,31 @@
|
||||
use crate::{
|
||||
accounts::Accounts,
|
||||
app_creation::setup_cc,
|
||||
app_size_handler::AppSizeHandler,
|
||||
app_style::{dark_mode, light_mode},
|
||||
args::Args,
|
||||
args::ColumnsArgs,
|
||||
column::Columns,
|
||||
decks::{Decks, DecksCache, FALLBACK_PUBKEY},
|
||||
draft::Drafts,
|
||||
filter::FilterState,
|
||||
frame_history::FrameHistory,
|
||||
imgcache::ImageCache,
|
||||
nav,
|
||||
notecache::NoteCache,
|
||||
notes_holder::NotesHolderStorage,
|
||||
profile::Profile,
|
||||
storage::{self, DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageType},
|
||||
storage,
|
||||
subscriptions::{SubKind, Subscriptions},
|
||||
support::Support,
|
||||
theme_handler::ThemeHandler,
|
||||
thread::Thread,
|
||||
timeline::{self, Timeline},
|
||||
ui::{self, is_compiled_as_mobile, DesktopSidePanel},
|
||||
unknowns::UnknownIds,
|
||||
ui::{self, DesktopSidePanel},
|
||||
unknowns,
|
||||
view_state::ViewState,
|
||||
Result,
|
||||
};
|
||||
|
||||
use notedeck::{Accounts, AppContext, DataPath, DataPathType, FilterState, ImageCache, UnknownIds};
|
||||
|
||||
use enostr::{ClientMessage, Keypair, Pubkey, RelayEvent, RelayMessage, RelayPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use egui::{Context, Frame, Style};
|
||||
use egui::{Frame, Style};
|
||||
use egui_extras::{Size, StripBuilder};
|
||||
|
||||
use nostrdb::{Config, Ndb, Transaction};
|
||||
use nostrdb::{Ndb, Transaction};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
@@ -48,26 +41,16 @@ pub enum DamusState {
|
||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||
pub struct Damus {
|
||||
state: DamusState,
|
||||
pub note_cache: NoteCache,
|
||||
pub pool: RelayPool,
|
||||
|
||||
pub decks_cache: DecksCache,
|
||||
pub ndb: Ndb,
|
||||
pub view_state: ViewState,
|
||||
pub unknown_ids: UnknownIds,
|
||||
pub drafts: Drafts,
|
||||
pub threads: NotesHolderStorage<Thread>,
|
||||
pub profiles: NotesHolderStorage<Profile>,
|
||||
pub img_cache: ImageCache,
|
||||
pub accounts: Accounts,
|
||||
pub subscriptions: Subscriptions,
|
||||
pub app_rect_handler: AppSizeHandler,
|
||||
pub support: Support,
|
||||
pub theme: ThemeHandler,
|
||||
|
||||
frame_history: crate::frame_history::FrameHistory,
|
||||
//frame_history: crate::frame_history::FrameHistory,
|
||||
|
||||
pub path: DataPath,
|
||||
// TODO: make these bitflags
|
||||
pub debug: bool,
|
||||
pub since_optimize: bool,
|
||||
@@ -99,21 +82,26 @@ fn handle_key_events(input: &egui::InputState, _pixels_per_point: f32, columns:
|
||||
}
|
||||
}
|
||||
|
||||
fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> {
|
||||
fn try_process_event(
|
||||
damus: &mut Damus,
|
||||
app_ctx: &mut AppContext<'_>,
|
||||
ctx: &egui::Context,
|
||||
) -> Result<()> {
|
||||
let ppp = ctx.pixels_per_point();
|
||||
let current_columns = get_active_columns_mut(&damus.accounts, &mut damus.decks_cache);
|
||||
let current_columns = get_active_columns_mut(app_ctx.accounts, &mut damus.decks_cache);
|
||||
ctx.input(|i| handle_key_events(i, ppp, current_columns));
|
||||
|
||||
let ctx2 = ctx.clone();
|
||||
let wakeup = move || {
|
||||
ctx2.request_repaint();
|
||||
};
|
||||
damus.pool.keepalive_ping(wakeup);
|
||||
|
||||
app_ctx.pool.keepalive_ping(wakeup);
|
||||
|
||||
// NOTE: we don't use the while let loop due to borrow issues
|
||||
#[allow(clippy::while_let_loop)]
|
||||
loop {
|
||||
let ev = if let Some(ev) = damus.pool.try_recv() {
|
||||
let ev = if let Some(ev) = app_ctx.pool.try_recv() {
|
||||
ev.into_owned()
|
||||
} else {
|
||||
break;
|
||||
@@ -121,16 +109,16 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> {
|
||||
|
||||
match (&ev.event).into() {
|
||||
RelayEvent::Opened => {
|
||||
damus
|
||||
app_ctx
|
||||
.accounts
|
||||
.send_initial_filters(&mut damus.pool, &ev.relay);
|
||||
.send_initial_filters(app_ctx.pool, &ev.relay);
|
||||
|
||||
timeline::send_initial_timeline_filters(
|
||||
&damus.ndb,
|
||||
app_ctx.ndb,
|
||||
damus.since_optimize,
|
||||
get_active_columns_mut(&damus.accounts, &mut damus.decks_cache),
|
||||
get_active_columns_mut(app_ctx.accounts, &mut damus.decks_cache),
|
||||
&mut damus.subscriptions,
|
||||
&mut damus.pool,
|
||||
app_ctx.pool,
|
||||
&ev.relay,
|
||||
);
|
||||
}
|
||||
@@ -138,35 +126,35 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> {
|
||||
RelayEvent::Closed => warn!("{} connection closed", &ev.relay),
|
||||
RelayEvent::Error(e) => error!("{}: {}", &ev.relay, e),
|
||||
RelayEvent::Other(msg) => trace!("other event {:?}", &msg),
|
||||
RelayEvent::Message(msg) => process_message(damus, &ev.relay, &msg),
|
||||
RelayEvent::Message(msg) => process_message(damus, app_ctx, &ev.relay, &msg),
|
||||
}
|
||||
}
|
||||
|
||||
let current_columns = get_active_columns_mut(&damus.accounts, &mut damus.decks_cache);
|
||||
let current_columns = get_active_columns_mut(app_ctx.accounts, &mut damus.decks_cache);
|
||||
let n_timelines = current_columns.timelines().len();
|
||||
for timeline_ind in 0..n_timelines {
|
||||
let is_ready = {
|
||||
let timeline = &mut current_columns.timelines[timeline_ind];
|
||||
timeline::is_timeline_ready(
|
||||
&damus.ndb,
|
||||
&mut damus.pool,
|
||||
&mut damus.note_cache,
|
||||
app_ctx.ndb,
|
||||
app_ctx.pool,
|
||||
app_ctx.note_cache,
|
||||
timeline,
|
||||
&damus.accounts.mutefun(),
|
||||
&app_ctx.accounts.mutefun(),
|
||||
)
|
||||
};
|
||||
|
||||
if is_ready {
|
||||
let txn = Transaction::new(&damus.ndb).expect("txn");
|
||||
let txn = Transaction::new(app_ctx.ndb).expect("txn");
|
||||
|
||||
if let Err(err) = Timeline::poll_notes_into_view(
|
||||
timeline_ind,
|
||||
current_columns.timelines_mut(),
|
||||
&damus.ndb,
|
||||
app_ctx.ndb,
|
||||
&txn,
|
||||
&mut damus.unknown_ids,
|
||||
&mut damus.note_cache,
|
||||
&damus.accounts.mutefun(),
|
||||
app_ctx.unknown_ids,
|
||||
app_ctx.note_cache,
|
||||
&app_ctx.accounts.mutefun(),
|
||||
) {
|
||||
error!("poll_notes_into_view: {err}");
|
||||
}
|
||||
@@ -175,22 +163,22 @@ fn try_process_event(damus: &mut Damus, ctx: &egui::Context) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
if damus.unknown_ids.ready_to_send() {
|
||||
unknown_id_send(damus);
|
||||
if app_ctx.unknown_ids.ready_to_send() {
|
||||
unknown_id_send(app_ctx.unknown_ids, app_ctx.pool);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unknown_id_send(damus: &mut Damus) {
|
||||
let filter = damus.unknown_ids.filter().expect("filter");
|
||||
fn unknown_id_send(unknown_ids: &mut UnknownIds, pool: &mut RelayPool) {
|
||||
let filter = unknown_ids.filter().expect("filter");
|
||||
info!(
|
||||
"Getting {} unknown ids from relays",
|
||||
damus.unknown_ids.ids().len()
|
||||
unknown_ids.ids().len()
|
||||
);
|
||||
let msg = ClientMessage::req("unknownids".to_string(), filter);
|
||||
damus.unknown_ids.clear();
|
||||
damus.pool.send(&msg);
|
||||
unknown_ids.clear();
|
||||
pool.send(&msg);
|
||||
}
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
@@ -198,8 +186,11 @@ fn setup_profiling() {
|
||||
puffin::set_scopes_on(true); // tell puffin to collect data
|
||||
}
|
||||
|
||||
fn update_damus(damus: &mut Damus, ctx: &egui::Context) {
|
||||
damus.accounts.update(&damus.ndb, &mut damus.pool, ctx); // update user relay and mute lists
|
||||
fn update_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>) {
|
||||
let _ctx = app_ctx.egui.clone();
|
||||
let ctx = &_ctx;
|
||||
|
||||
app_ctx.accounts.update(app_ctx.ndb, app_ctx.pool, ctx); // update user relay and mute lists
|
||||
|
||||
match damus.state {
|
||||
DamusState::Initializing => {
|
||||
@@ -212,10 +203,10 @@ fn update_damus(damus: &mut Damus, ctx: &egui::Context) {
|
||||
.subscriptions()
|
||||
.insert("unknownids".to_string(), SubKind::OneShot);
|
||||
if let Err(err) = timeline::setup_initial_nostrdb_subs(
|
||||
&damus.ndb,
|
||||
&mut damus.note_cache,
|
||||
app_ctx.ndb,
|
||||
app_ctx.note_cache,
|
||||
&mut damus.decks_cache,
|
||||
&damus.accounts.mutefun(),
|
||||
&app_ctx.accounts.mutefun(),
|
||||
) {
|
||||
warn!("update_damus init: {err}");
|
||||
}
|
||||
@@ -224,24 +215,27 @@ fn update_damus(damus: &mut Damus, ctx: &egui::Context) {
|
||||
DamusState::Initialized => (),
|
||||
};
|
||||
|
||||
if let Err(err) = try_process_event(damus, ctx) {
|
||||
if let Err(err) = try_process_event(damus, app_ctx, ctx) {
|
||||
error!("error processing event: {}", err);
|
||||
}
|
||||
|
||||
damus.app_rect_handler.try_save_app_size(ctx);
|
||||
}
|
||||
|
||||
fn process_event(damus: &mut Damus, _subid: &str, event: &str) {
|
||||
fn process_event(ndb: &Ndb, _subid: &str, event: &str) {
|
||||
#[cfg(feature = "profiling")]
|
||||
puffin::profile_function!();
|
||||
|
||||
//info!("processing event {}", event);
|
||||
if let Err(_err) = damus.ndb.process_event(event) {
|
||||
if let Err(_err) = ndb.process_event(event) {
|
||||
error!("error processing event {}", event);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_eose(damus: &mut Damus, subid: &str, relay_url: &str) -> Result<()> {
|
||||
fn handle_eose(
|
||||
damus: &mut Damus,
|
||||
ctx: &mut AppContext<'_>,
|
||||
subid: &str,
|
||||
relay_url: &str,
|
||||
) -> Result<()> {
|
||||
let sub_kind = if let Some(sub_kind) = damus.subscriptions().get(subid) {
|
||||
sub_kind
|
||||
} else {
|
||||
@@ -258,29 +252,29 @@ fn handle_eose(damus: &mut Damus, subid: &str, relay_url: &str) -> Result<()> {
|
||||
// eose on timeline? whatevs
|
||||
}
|
||||
SubKind::Initial => {
|
||||
let txn = Transaction::new(&damus.ndb)?;
|
||||
UnknownIds::update(
|
||||
let txn = Transaction::new(ctx.ndb)?;
|
||||
unknowns::update_from_columns(
|
||||
&txn,
|
||||
&mut damus.unknown_ids,
|
||||
get_active_columns(&damus.accounts, &damus.decks_cache),
|
||||
&damus.ndb,
|
||||
&mut damus.note_cache,
|
||||
ctx.unknown_ids,
|
||||
get_active_columns(ctx.accounts, &damus.decks_cache),
|
||||
ctx.ndb,
|
||||
ctx.note_cache,
|
||||
);
|
||||
// this is possible if this is the first time
|
||||
if damus.unknown_ids.ready_to_send() {
|
||||
unknown_id_send(damus);
|
||||
if ctx.unknown_ids.ready_to_send() {
|
||||
unknown_id_send(ctx.unknown_ids, ctx.pool);
|
||||
}
|
||||
}
|
||||
|
||||
// oneshot subs just close when they're done
|
||||
SubKind::OneShot => {
|
||||
let msg = ClientMessage::close(subid.to_string());
|
||||
damus.pool.send_to(&msg, relay_url);
|
||||
ctx.pool.send_to(&msg, relay_url);
|
||||
}
|
||||
|
||||
SubKind::FetchingContactList(timeline_uid) => {
|
||||
let timeline = if let Some(tl) =
|
||||
get_active_columns_mut(&damus.accounts, &mut damus.decks_cache)
|
||||
get_active_columns_mut(ctx.accounts, &mut damus.decks_cache)
|
||||
.find_timeline_mut(timeline_uid)
|
||||
{
|
||||
tl
|
||||
@@ -326,27 +320,28 @@ fn handle_eose(damus: &mut Damus, subid: &str, relay_url: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_message(damus: &mut Damus, relay: &str, msg: &RelayMessage) {
|
||||
fn process_message(damus: &mut Damus, ctx: &mut AppContext<'_>, relay: &str, msg: &RelayMessage) {
|
||||
match msg {
|
||||
RelayMessage::Event(subid, ev) => process_event(damus, subid, ev),
|
||||
RelayMessage::Event(subid, ev) => process_event(ctx.ndb, subid, ev),
|
||||
RelayMessage::Notice(msg) => warn!("Notice from {}: {}", relay, msg),
|
||||
RelayMessage::OK(cr) => info!("OK {:?}", cr),
|
||||
RelayMessage::Eose(sid) => {
|
||||
if let Err(err) = handle_eose(damus, sid, relay) {
|
||||
if let Err(err) = handle_eose(damus, ctx, sid, relay) {
|
||||
error!("error handling eose: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_damus(damus: &mut Damus, ctx: &Context) {
|
||||
if ui::is_narrow(ctx) {
|
||||
render_damus_mobile(ctx, damus);
|
||||
fn render_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>) {
|
||||
if notedeck::ui::is_narrow(app_ctx.egui) {
|
||||
render_damus_mobile(damus, app_ctx);
|
||||
} else {
|
||||
render_damus_desktop(ctx, damus);
|
||||
render_damus_desktop(damus, app_ctx);
|
||||
}
|
||||
|
||||
ctx.request_repaint_after(Duration::from_secs(1));
|
||||
// We use this for keeping timestamps and things up to date
|
||||
app_ctx.egui.request_repaint_after(Duration::from_secs(1));
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
puffin_egui::profiler_window(ctx);
|
||||
@@ -373,91 +368,12 @@ fn determine_key_storage_type() -> KeyStorageType {
|
||||
|
||||
impl Damus {
|
||||
/// Called once before the first frame.
|
||||
pub fn new<P: AsRef<Path>>(ctx: &egui::Context, data_path: P, args: Vec<String>) -> Self {
|
||||
pub fn new(ctx: &mut AppContext<'_>, args: &[String]) -> Self {
|
||||
// arg parsing
|
||||
let parsed_args = Args::parse(&args);
|
||||
let is_mobile = parsed_args.is_mobile.unwrap_or(ui::is_compiled_as_mobile());
|
||||
|
||||
// Some people have been running notedeck in debug, let's catch that!
|
||||
if !cfg!(test) && cfg!(debug_assertions) && !parsed_args.debug {
|
||||
println!("--- WELCOME TO DAMUS NOTEDECK! ---");
|
||||
println!("It looks like are running notedeck in debug mode, unless you are a developer, this is not likely what you want.");
|
||||
println!("If you are a developer, run `cargo run -- --debug` to skip this message.");
|
||||
println!("For everyone else, try again with `cargo run --release`. Enjoy!");
|
||||
println!("---------------------------------");
|
||||
panic!();
|
||||
}
|
||||
|
||||
setup_cc(ctx, is_mobile, parsed_args.light);
|
||||
|
||||
let data_path = parsed_args
|
||||
.datapath
|
||||
.unwrap_or(data_path.as_ref().to_str().expect("db path ok").to_string());
|
||||
let path = DataPath::new(&data_path);
|
||||
let dbpath_str = parsed_args
|
||||
.dbpath
|
||||
.unwrap_or_else(|| path.path(DataPathType::Db).to_str().unwrap().to_string());
|
||||
|
||||
let _ = std::fs::create_dir_all(&dbpath_str);
|
||||
|
||||
let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir());
|
||||
let _ = std::fs::create_dir_all(imgcache_dir.clone());
|
||||
|
||||
let mapsize = if cfg!(target_os = "windows") {
|
||||
// 16 Gib on windows because it actually creates the file
|
||||
1024usize * 1024usize * 1024usize * 16usize
|
||||
} else {
|
||||
// 1 TiB for everything else since its just virtually mapped
|
||||
1024usize * 1024usize * 1024usize * 1024usize
|
||||
};
|
||||
|
||||
let theme = ThemeHandler::new(&path);
|
||||
ctx.options_mut(|o| {
|
||||
let cur_theme = theme.load();
|
||||
info!("Loaded theme {:?} from disk", cur_theme);
|
||||
o.theme_preference = cur_theme;
|
||||
});
|
||||
ctx.set_visuals_of(egui::Theme::Dark, dark_mode(is_compiled_as_mobile()));
|
||||
ctx.set_visuals_of(egui::Theme::Light, light_mode());
|
||||
|
||||
let config = Config::new().set_ingester_threads(4).set_mapsize(mapsize);
|
||||
|
||||
let keystore = if parsed_args.use_keystore {
|
||||
let keys_path = path.path(DataPathType::Keys);
|
||||
let selected_key_path = path.path(DataPathType::SelectedKey);
|
||||
KeyStorageType::FileSystem(FileKeyStorage::new(
|
||||
Directory::new(keys_path),
|
||||
Directory::new(selected_key_path),
|
||||
))
|
||||
} else {
|
||||
KeyStorageType::None
|
||||
};
|
||||
|
||||
let mut accounts = Accounts::new(keystore, parsed_args.relays);
|
||||
|
||||
let num_keys = parsed_args.keys.len();
|
||||
|
||||
let mut unknown_ids = UnknownIds::default();
|
||||
let ndb = Ndb::new(&dbpath_str, &config).expect("ndb");
|
||||
|
||||
{
|
||||
let txn = Transaction::new(&ndb).expect("txn");
|
||||
for key in parsed_args.keys {
|
||||
info!("adding account: {}", key.pubkey);
|
||||
accounts
|
||||
.add_account(key)
|
||||
.process_action(&mut unknown_ids, &ndb, &txn);
|
||||
}
|
||||
}
|
||||
|
||||
if num_keys != 0 {
|
||||
accounts.select_account(0);
|
||||
}
|
||||
|
||||
// AccountManager will setup the pool on first update
|
||||
let pool = RelayPool::new();
|
||||
|
||||
let account = accounts
|
||||
let parsed_args = ColumnsArgs::parse(args);
|
||||
let account = ctx
|
||||
.accounts
|
||||
.get_selected_account()
|
||||
.as_ref()
|
||||
.map(|a| a.pubkey.bytes());
|
||||
@@ -466,19 +382,19 @@ impl Damus {
|
||||
info!("DecksCache: loading from command line arguments");
|
||||
let mut columns: Columns = Columns::new();
|
||||
for col in parsed_args.columns {
|
||||
if let Some(timeline) = col.into_timeline(&ndb, account) {
|
||||
if let Some(timeline) = col.into_timeline(ctx.ndb, account) {
|
||||
columns.add_new_timeline_column(timeline);
|
||||
}
|
||||
}
|
||||
|
||||
columns_to_decks_cache(columns, account)
|
||||
} else if let Some(decks_cache) = storage::load_decks_cache(&path, &ndb) {
|
||||
} else if let Some(decks_cache) = crate::storage::load_decks_cache(ctx.path, ctx.ndb) {
|
||||
info!(
|
||||
"DecksCache: loading from disk {}",
|
||||
crate::storage::DECKS_CACHE_FILE
|
||||
);
|
||||
decks_cache
|
||||
} else if let Some(cols) = storage::deserialize_columns(&path, &ndb, account) {
|
||||
} else if let Some(cols) = storage::deserialize_columns(ctx.path, ctx.ndb, account) {
|
||||
info!(
|
||||
"DecksCache: loading from disk at depreciated location {}",
|
||||
crate::storage::COLUMNS_FILE
|
||||
@@ -486,79 +402,40 @@ impl Damus {
|
||||
columns_to_decks_cache(cols, account)
|
||||
} else {
|
||||
info!("DecksCache: creating new with demo configuration");
|
||||
let mut cache = DecksCache::new_with_demo_config(&ndb);
|
||||
for account in accounts.get_accounts() {
|
||||
let mut cache = DecksCache::new_with_demo_config(ctx.ndb);
|
||||
for account in ctx.accounts.get_accounts() {
|
||||
cache.add_deck_default(account.pubkey);
|
||||
}
|
||||
set_demo(&mut cache, &ndb, &mut accounts, &mut unknown_ids);
|
||||
set_demo(&mut cache, ctx.ndb, ctx.accounts, ctx.unknown_ids);
|
||||
|
||||
cache
|
||||
};
|
||||
|
||||
let debug = parsed_args.debug;
|
||||
|
||||
let app_rect_handler = AppSizeHandler::new(&path);
|
||||
let support = Support::new(&path);
|
||||
let debug = ctx.args.debug;
|
||||
let support = Support::new(ctx.path);
|
||||
|
||||
Self {
|
||||
pool,
|
||||
debug,
|
||||
unknown_ids,
|
||||
subscriptions: Subscriptions::default(),
|
||||
since_optimize: parsed_args.since_optimize,
|
||||
threads: NotesHolderStorage::default(),
|
||||
profiles: NotesHolderStorage::default(),
|
||||
drafts: Drafts::default(),
|
||||
state: DamusState::Initializing,
|
||||
img_cache: ImageCache::new(imgcache_dir),
|
||||
note_cache: NoteCache::default(),
|
||||
textmode: parsed_args.textmode,
|
||||
ndb,
|
||||
accounts,
|
||||
frame_history: FrameHistory::default(),
|
||||
//frame_history: FrameHistory::default(),
|
||||
view_state: ViewState::default(),
|
||||
path,
|
||||
app_rect_handler,
|
||||
support,
|
||||
decks_cache,
|
||||
theme,
|
||||
debug,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pool_mut(&mut self) -> &mut RelayPool {
|
||||
&mut self.pool
|
||||
pub fn columns_mut(&mut self, accounts: &Accounts) -> &mut Columns {
|
||||
get_active_columns_mut(accounts, &mut self.decks_cache)
|
||||
}
|
||||
|
||||
pub fn ndb(&self) -> &Ndb {
|
||||
&self.ndb
|
||||
}
|
||||
|
||||
pub fn drafts_mut(&mut self) -> &mut Drafts {
|
||||
&mut self.drafts
|
||||
}
|
||||
|
||||
pub fn img_cache_mut(&mut self) -> &mut ImageCache {
|
||||
&mut self.img_cache
|
||||
}
|
||||
|
||||
pub fn accounts(&self) -> &Accounts {
|
||||
&self.accounts
|
||||
}
|
||||
|
||||
pub fn accounts_mut(&mut self) -> &mut Accounts {
|
||||
&mut self.accounts
|
||||
}
|
||||
|
||||
pub fn view_state_mut(&mut self) -> &mut ViewState {
|
||||
&mut self.view_state
|
||||
}
|
||||
|
||||
pub fn columns_mut(&mut self) -> &mut Columns {
|
||||
get_active_columns_mut(&self.accounts, &mut self.decks_cache)
|
||||
}
|
||||
|
||||
pub fn columns(&self) -> &Columns {
|
||||
get_active_columns(&self.accounts, &self.decks_cache)
|
||||
pub fn columns(&self, accounts: &Accounts) -> &Columns {
|
||||
get_active_columns(accounts, &self.decks_cache)
|
||||
}
|
||||
|
||||
pub fn gen_subid(&self, kind: &SubKind) -> String {
|
||||
@@ -573,44 +450,25 @@ impl Damus {
|
||||
let decks_cache = DecksCache::default();
|
||||
|
||||
let path = DataPath::new(&data_path);
|
||||
let theme = ThemeHandler::new(&path);
|
||||
let imgcache_dir = path.path(DataPathType::Cache).join(ImageCache::rel_dir());
|
||||
let _ = std::fs::create_dir_all(imgcache_dir.clone());
|
||||
let debug = true;
|
||||
|
||||
let app_rect_handler = AppSizeHandler::new(&path);
|
||||
let support = Support::new(&path);
|
||||
|
||||
let config = Config::new().set_ingester_threads(2);
|
||||
|
||||
Self {
|
||||
debug,
|
||||
unknown_ids: UnknownIds::default(),
|
||||
subscriptions: Subscriptions::default(),
|
||||
since_optimize: true,
|
||||
threads: NotesHolderStorage::default(),
|
||||
profiles: NotesHolderStorage::default(),
|
||||
drafts: Drafts::default(),
|
||||
state: DamusState::Initializing,
|
||||
pool: RelayPool::new(),
|
||||
img_cache: ImageCache::new(imgcache_dir),
|
||||
note_cache: NoteCache::default(),
|
||||
textmode: false,
|
||||
ndb: Ndb::new(
|
||||
path.path(DataPathType::Db)
|
||||
.to_str()
|
||||
.expect("db path should be ok"),
|
||||
&config,
|
||||
)
|
||||
.expect("ndb"),
|
||||
accounts: Accounts::new(KeyStorageType::None, vec![]),
|
||||
frame_history: FrameHistory::default(),
|
||||
//frame_history: FrameHistory::default(),
|
||||
view_state: ViewState::default(),
|
||||
path,
|
||||
app_rect_handler,
|
||||
support,
|
||||
decks_cache,
|
||||
theme,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,14 +476,6 @@ impl Damus {
|
||||
&mut self.subscriptions.subs
|
||||
}
|
||||
|
||||
pub fn note_cache_mut(&mut self) -> &mut NoteCache {
|
||||
&mut self.note_cache
|
||||
}
|
||||
|
||||
pub fn unknown_ids_mut(&mut self) -> &mut UnknownIds {
|
||||
&mut self.unknown_ids
|
||||
}
|
||||
|
||||
pub fn threads(&self) -> &NotesHolderStorage<Thread> {
|
||||
&self.threads
|
||||
}
|
||||
@@ -633,10 +483,6 @@ impl Damus {
|
||||
pub fn threads_mut(&mut self) -> &mut NotesHolderStorage<Thread> {
|
||||
&mut self.threads
|
||||
}
|
||||
|
||||
pub fn note_cache(&self) -> &NoteCache {
|
||||
&self.note_cache
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -648,17 +494,20 @@ fn circle_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {
|
||||
}
|
||||
*/
|
||||
|
||||
fn render_damus_mobile(ctx: &egui::Context, app: &mut Damus) {
|
||||
fn render_damus_mobile(app: &mut Damus, app_ctx: &mut AppContext<'_>) {
|
||||
let _ctx = app_ctx.egui.clone();
|
||||
let ctx = &_ctx;
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
puffin::profile_function!();
|
||||
|
||||
//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).process_render_nav_response(app)
|
||||
main_panel(&ctx.style(), notedeck::ui::is_narrow(ctx)).show(ctx, |ui| {
|
||||
if !app.columns(app_ctx.accounts).columns().is_empty()
|
||||
&& nav::render_nav(0, app, app_ctx, ui).process_render_nav_response(app, app_ctx)
|
||||
{
|
||||
storage::save_decks_cache(&app.path, &app.decks_cache);
|
||||
storage::save_decks_cache(app_ctx.path, &app.decks_cache);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -677,13 +526,16 @@ fn main_panel(style: &Style, narrow: bool) -> egui::CentralPanel {
|
||||
})
|
||||
}
|
||||
|
||||
fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) {
|
||||
fn render_damus_desktop(app: &mut Damus, app_ctx: &mut AppContext<'_>) {
|
||||
let _ctx = app_ctx.egui.clone();
|
||||
let ctx = &_ctx;
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
puffin::profile_function!();
|
||||
|
||||
let screen_size = ctx.screen_rect().width();
|
||||
let calc_panel_width = (screen_size
|
||||
/ get_active_columns(&app.accounts, &app.decks_cache).num_columns() as f32)
|
||||
/ get_active_columns(app_ctx.accounts, &app.decks_cache).num_columns() as f32)
|
||||
- 30.0;
|
||||
let min_width = 320.0;
|
||||
let need_scroll = calc_panel_width < min_width;
|
||||
@@ -693,24 +545,24 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) {
|
||||
Size::remainder()
|
||||
};
|
||||
|
||||
main_panel(&ctx.style(), ui::is_narrow(ctx)).show(ctx, |ui| {
|
||||
main_panel(&ctx.style(), notedeck::ui::is_narrow(ctx)).show(ctx, |ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
if need_scroll {
|
||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||
timelines_view(ui, panel_sizes, app);
|
||||
timelines_view(ui, panel_sizes, app, app_ctx);
|
||||
});
|
||||
} else {
|
||||
timelines_view(ui, panel_sizes, app);
|
||||
timelines_view(ui, panel_sizes, app, app_ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
||||
fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, ctx: &mut AppContext<'_>) {
|
||||
StripBuilder::new(ui)
|
||||
.size(Size::exact(ui::side_panel::SIDE_PANEL_WIDTH))
|
||||
.sizes(
|
||||
sizes,
|
||||
get_active_columns(&app.accounts, &app.decks_cache).num_columns(),
|
||||
get_active_columns(ctx.accounts, &app.decks_cache).num_columns(),
|
||||
)
|
||||
.clip(true)
|
||||
.horizontal(|mut strip| {
|
||||
@@ -718,9 +570,9 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
||||
strip.cell(|ui| {
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
let side_panel = DesktopSidePanel::new(
|
||||
&app.ndb,
|
||||
&mut app.img_cache,
|
||||
app.accounts.get_selected_account(),
|
||||
ctx.ndb,
|
||||
ctx.img_cache,
|
||||
ctx.accounts.get_selected_account(),
|
||||
&app.decks_cache,
|
||||
)
|
||||
.show(ui);
|
||||
@@ -728,9 +580,9 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
||||
if side_panel.response.clicked() || side_panel.response.secondary_clicked() {
|
||||
if let Some(action) = DesktopSidePanel::perform_action(
|
||||
&mut app.decks_cache,
|
||||
&app.accounts,
|
||||
ctx.accounts,
|
||||
&mut app.support,
|
||||
&mut app.theme,
|
||||
ctx.theme,
|
||||
side_panel.action,
|
||||
) {
|
||||
side_panel_action = Some(action);
|
||||
@@ -747,15 +599,15 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
||||
|
||||
let mut save_cols = false;
|
||||
if let Some(action) = side_panel_action {
|
||||
save_cols = save_cols || action.process(app);
|
||||
save_cols = save_cols || action.process(app, ctx);
|
||||
}
|
||||
|
||||
let num_cols = app.columns().num_columns();
|
||||
let num_cols = app.columns(ctx.accounts).num_columns();
|
||||
let mut responses = Vec::with_capacity(num_cols);
|
||||
for col_index in 0..num_cols {
|
||||
strip.cell(|ui| {
|
||||
let rect = ui.available_rect_before_wrap();
|
||||
responses.push(nav::render_nav(col_index, app, ui));
|
||||
responses.push(nav::render_nav(col_index, app, ctx, ui));
|
||||
|
||||
// vertical line
|
||||
ui.painter().vline(
|
||||
@@ -769,27 +621,23 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus) {
|
||||
}
|
||||
|
||||
for response in responses {
|
||||
let save = response.process_render_nav_response(app);
|
||||
let save = response.process_render_nav_response(app, ctx);
|
||||
save_cols = save_cols || save;
|
||||
}
|
||||
|
||||
if save_cols {
|
||||
storage::save_decks_cache(&app.path, &app.decks_cache);
|
||||
storage::save_decks_cache(ctx.path, &app.decks_cache);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl eframe::App for Damus {
|
||||
/// Called by the frame work to save state before shutdown.
|
||||
fn save(&mut self, _storage: &mut dyn eframe::Storage) {
|
||||
//eframe::set_value(storage, eframe::APP_KEY, self);
|
||||
}
|
||||
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
|
||||
self.frame_history
|
||||
impl notedeck::App for Damus {
|
||||
fn update(&mut self, ctx: &mut AppContext<'_>) {
|
||||
/*
|
||||
self.app
|
||||
.frame_history
|
||||
.on_new_frame(ctx.input(|i| i.time), frame.info().cpu_usage);
|
||||
*/
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
puffin::GlobalProfiler::lock().new_frame();
|
||||
|
||||
Reference in New Issue
Block a user