notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit 26c4d90be38f5b12f34d76d591a0a256d5eb100a
parent a6856867a992e3ecbffb8d1224405a0382e2cb31
Author: William Casarin <jb55@jb55.com>
Date:   Tue, 25 Jun 2024 13:16:13 -0500

initial postbox for testing

not sure if we want to put this here yet, but it matches the design
and will be useful for testing

Fixes: https://github.com/damus-io/notedeck/issues/110
Suggested-by: Rob

Diffstat:
Msrc/app.rs | 102+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/draft.rs | 22++++++++++++++++++++++
Msrc/ui/note/post.rs | 27++++++++++++++-------------
Msrc/ui/note/reply.rs | 4+++-
4 files changed, 97 insertions(+), 58 deletions(-)

diff --git a/src/app.rs b/src/app.rs @@ -1,7 +1,7 @@ use crate::account_manager::AccountManager; use crate::app_creation::setup_cc; use crate::app_style::user_requested_visuals_change; -use crate::draft::Draft; +use crate::draft::{DraftSource, Drafts}; use crate::error::Error; use crate::frame_history::FrameHistory; use crate::imgcache::ImageCache; @@ -16,7 +16,6 @@ use crate::Result; use egui_nav::{Nav, NavAction}; use enostr::RelayPool; use std::cell::RefCell; -use std::collections::HashMap; use std::rc::Rc; use egui::{Context, Frame, Style}; @@ -48,10 +47,10 @@ pub struct Damus { /// global navigation for account management popups, etc. pub global_nav: Vec<Route>, pub textmode: bool, - pub drafts: HashMap<enostr::NoteId, Draft>, pub timelines: Vec<Timeline>, pub selected_timeline: i32, + pub drafts: Drafts, pub img_cache: ImageCache, pub ndb: Ndb, @@ -711,7 +710,7 @@ impl Damus { Self { is_mobile, - drafts: HashMap::new(), + drafts: Drafts::default(), state: DamusState::Initializing, pool: RelayPool::new(), img_cache: ImageCache::new(imgcache_dir), @@ -746,7 +745,7 @@ impl Damus { config.set_ingester_threads(2); Self { is_mobile, - drafts: HashMap::new(), + drafts: Drafts::default(), state: DamusState::Initializing, pool: RelayPool::new(), img_cache: ImageCache::new(imgcache_dir), @@ -891,53 +890,68 @@ fn render_nav(routes: Vec<Route>, timeline_ind: usize, app: &mut Damus, ui: &mut let navigating = app.timelines[timeline_ind].navigating; let app_ctx = Rc::new(RefCell::new(app)); - let nav_response = - Nav::new(routes) - .navigating(navigating) - .show(ui, |ui, nav| match nav.top() { - Route::Timeline(_n) => { - timeline::timeline_view(ui, &mut app_ctx.borrow_mut(), timeline_ind); - } + let nav_response = Nav::new(routes) + .navigating(navigating) + .title(false) + .show(ui, |ui, nav| match nav.top() { + Route::Timeline(_n) => { + let app = &mut app_ctx.borrow_mut(); - Route::ManageAccount => { - ui.label("account management view"); - } + if timeline_ind == 0 { + // show a postbox in the first timeline - Route::Thread(_key) => { - ui.label("thread view"); - } + // TODO: don't show the post box if we have no accounts + let poster = app + .account_manager + .get_selected_account_index() + .unwrap_or(0); - Route::Relays => { - let pool = &mut app_ctx.borrow_mut().pool; - let manager = RelayPoolManager::new(pool); - RelayView::new(manager).ui(ui); + if let Ok(txn) = Transaction::new(&app.ndb) { + ui::PostView::new(app, DraftSource::Compose, poster).ui(&txn, ui); + } } + timeline::timeline_view(ui, app, timeline_ind); + } - Route::Reply(id) => { - let mut app = app_ctx.borrow_mut(); + Route::ManageAccount => { + ui.label("account management view"); + } - let txn = if let Ok(txn) = Transaction::new(&app.ndb) { - txn - } else { - ui.label("Reply to unknown note"); - return; - }; + Route::Thread(_key) => { + ui.label("thread view"); + } - let note = if let Ok(note) = app.ndb.get_note_by_id(&txn, id.bytes()) { - note - } else { - ui.label("Reply to unknown note"); - return; - }; + Route::Relays => { + let pool = &mut app_ctx.borrow_mut().pool; + let manager = RelayPoolManager::new(pool); + RelayView::new(manager).ui(ui); + } - let id = egui::Id::new(("post", timeline_ind, note.key().unwrap())); - egui::ScrollArea::vertical().show(ui, |ui| { - ui::PostReplyView::new(&mut app, &note) - .id_source(id) - .show(ui); - }); - } - }); + Route::Reply(id) => { + let mut app = app_ctx.borrow_mut(); + + let txn = if let Ok(txn) = Transaction::new(&app.ndb) { + txn + } else { + ui.label("Reply to unknown note"); + return; + }; + + let note = if let Ok(note) = app.ndb.get_note_by_id(&txn, id.bytes()) { + note + } else { + ui.label("Reply to unknown note"); + return; + }; + + let id = egui::Id::new(("post", timeline_ind, note.key().unwrap())); + egui::ScrollArea::vertical().show(ui, |ui| { + ui::PostReplyView::new(&mut app, &note) + .id_source(id) + .show(ui); + }); + } + }); if let Some(NavAction::Returned) = nav_response.action { app_ctx.borrow_mut().timelines[timeline_ind].routes.pop(); diff --git a/src/draft.rs b/src/draft.rs @@ -1,8 +1,30 @@ +use std::collections::HashMap; + #[derive(Default)] pub struct Draft { pub buffer: String, } +#[derive(Default)] +pub struct Drafts { + pub replies: HashMap<[u8; 32], Draft>, + pub compose: Draft, +} + +pub enum DraftSource<'a> { + Compose, + Reply(&'a [u8; 32]), // note id +} + +impl<'a> DraftSource<'a> { + pub fn draft(&self, drafts: &'a mut Drafts) -> &'a mut Draft { + match self { + DraftSource::Compose => &mut drafts.compose, + DraftSource::Reply(id) => drafts.replies.entry(**id).or_default(), + } + } +} + impl Draft { pub fn new() -> Self { Draft::default() diff --git a/src/ui/note/post.rs b/src/ui/note/post.rs @@ -1,16 +1,16 @@ use crate::app::Damus; -use crate::draft::Draft; +use crate::draft::{Draft, DraftSource}; use crate::ui; use crate::ui::{Preview, PreviewConfig, View}; use egui::widgets::text_edit::TextEdit; use nostrdb::Transaction; -pub struct PostView<'app, 'p> { +pub struct PostView<'app, 'd> { app: &'app mut Damus, /// account index poster: usize, + draft_source: DraftSource<'d>, id_source: Option<egui::Id>, - replying_to: &'p [u8; 32], } pub struct NewPost { @@ -27,14 +27,14 @@ pub struct PostResponse { pub edit_response: egui::Response, } -impl<'app, 'p> PostView<'app, 'p> { - pub fn new(app: &'app mut Damus, poster: usize, replying_to: &'p [u8; 32]) -> Self { +impl<'app, 'd> PostView<'app, 'd> { + pub fn new(app: &'app mut Damus, draft_source: DraftSource<'d>, poster: usize) -> Self { let id_source: Option<egui::Id> = None; PostView { id_source, app, poster, - replying_to, + draft_source, } } @@ -44,10 +44,7 @@ impl<'app, 'p> PostView<'app, 'p> { } fn draft(&mut self) -> &mut Draft { - self.app - .drafts - .entry(enostr::NoteId::new(*self.replying_to)) - .or_default() + self.draft_source.draft(&mut self.app.drafts) } fn editbox(&mut self, txn: &nostrdb::Transaction, ui: &mut egui::Ui) -> egui::Response { @@ -82,7 +79,12 @@ impl<'app, 'p> PostView<'app, 'p> { ); } - let response = ui.add(TextEdit::multiline(&mut self.draft().buffer).frame(false)); + let buffer = &mut self.draft_source.draft(&mut self.app.drafts).buffer; + let response = ui.add( + TextEdit::multiline(buffer) + .hint_text(egui::RichText::new("Write a banger note here...").weak()) + .frame(false), + ); let focused = response.has_focus(); @@ -183,9 +185,8 @@ mod preview { impl View for PostPreview { fn ui(&mut self, ui: &mut egui::Ui) { - let test_note_id = test_data::test_pubkey(); let txn = Transaction::new(&self.app.ndb).unwrap(); - PostView::new(&mut self.app, 0, test_note_id).ui(&txn, ui); + PostView::new(&mut self.app, DraftSource::Compose, 0).ui(&txn, ui); } } diff --git a/src/ui/note/reply.rs b/src/ui/note/reply.rs @@ -1,3 +1,4 @@ +use crate::draft::DraftSource; use crate::{ui, Damus}; pub struct PostReplyView<'a> { @@ -58,7 +59,8 @@ impl<'a> PostReplyView<'a> { let rect_before_post = ui.min_rect(); let id = self.id(); - let post_response = ui::PostView::new(self.app, poster, replying_to) + let draft_source = DraftSource::Reply(replying_to); + let post_response = ui::PostView::new(self.app, draft_source, poster) .id_source(id) .ui(self.note.txn().unwrap(), ui);