commit 0dd33c90e7664d934f0530cf8499d6bc193c436a
parent bff0f3f628ce31ee141a7f71ea546b76dba38fed
Author: William Casarin <jb55@jb55.com>
Date: Tue, 4 Jun 2024 01:51:30 -0500
initial navigation
Diffstat:
16 files changed, 529 insertions(+), 577 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -1036,15 +1036,6 @@ dependencies = [
]
[[package]]
-name = "egui-tabs"
-version = "0.1.0"
-source = "git+https://github.com/damus-io/egui-tabs?rev=75f47141aebcf876986fad00dd83a69a7bb04840#75f47141aebcf876986fad00dd83a69a7bb04840"
-dependencies = [
- "egui",
- "egui_extras",
-]
-
-[[package]]
name = "egui-wgpu"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1111,6 +1102,25 @@ dependencies = [
"puffin",
"wasm-bindgen",
"web-sys",
+ "winit",
+]
+
+[[package]]
+name = "egui_nav"
+version = "0.1.0"
+source = "git+https://github.com/damus-io/egui-nav?rev=9f640df83494a79cd7aa0b5983c83290d284d6ee#9f640df83494a79cd7aa0b5983c83290d284d6ee"
+dependencies = [
+ "egui",
+ "egui_extras",
+]
+
+[[package]]
+name = "egui_tabs"
+version = "0.1.0"
+source = "git+https://github.com/damus-io/egui-tabs?rev=120971fc43db6ba0b6f194f4bd4a66f7e00a4e22#120971fc43db6ba0b6f194f4bd4a66f7e00a4e22"
+dependencies = [
+ "egui",
+ "egui_extras",
]
[[package]]
@@ -1178,7 +1188,6 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
- "shatter",
"tracing",
]
@@ -2454,8 +2463,9 @@ dependencies = [
"console_error_panic_hook",
"eframe",
"egui",
- "egui-tabs",
"egui_extras",
+ "egui_nav",
+ "egui_tabs",
"egui_virtual_list",
"ehttp 0.2.0",
"enostr",
@@ -3525,16 +3535,6 @@ dependencies = [
]
[[package]]
-name = "shatter"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c72d316f805789a3a026fd327dd854ece0fbdc5fb4f57ac9d1319f3670a4177"
-dependencies = [
- "env_logger 0.10.2",
- "log",
-]
-
-[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
@@ -14,12 +14,25 @@ crate-type = ["lib", "cdylib"]
[dependencies]
#egui-android = { git = "https://github.com/jb55/egui-android.git" }
egui = "0.27.2"
-eframe = { version = "0.27.2", default-features = false, features = [ "glow", "wgpu", "android-native-activity" ] }
+eframe = { version = "0.27.2", default-features = false, features = [ "glow", "wgpu", "x11", "wayland", "android-native-activity" ] }
+
+ #
+ # TODO default features:
+ #
+ #"accesskit",
+ #"default_fonts",
+ #"glow",
+ #"wayland",
+ #"web_screen_reader",
+ #"winit/default",
+ #"x11",
+
#eframe = { version = "0.27.2", default-features = false, features = [ "glow", "android-native-activity" ] }
#eframe = "0.22.0"
egui_extras = { version = "0.27.2", features = ["all_loaders"] }
ehttp = "0.2.0"
-egui-tabs = { git = "https://github.com/damus-io/egui-tabs", rev = "75f47141aebcf876986fad00dd83a69a7bb04840" }
+egui_tabs = { git = "https://github.com/damus-io/egui-tabs", rev = "120971fc43db6ba0b6f194f4bd4a66f7e00a4e22" }
+egui_nav = { git = "https://github.com/damus-io/egui-nav", rev = "9f640df83494a79cd7aa0b5983c83290d284d6ee" }
reqwest = { version = "0.12.4", default-features = false, features = [ "rustls-tls-native-roots" ] }
image = { version = "0.24", features = ["jpeg", "png", "webp"] }
log = "0.4.17"
diff --git a/enostr/Cargo.toml b/enostr/Cargo.toml
@@ -11,7 +11,6 @@ serde_derive = "1"
serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence
serde_json = "1.0.89"
tracing = "0.1.37"
-shatter = "0.1.1"
nostr = { version = "0.30.0" }
hex = "0.4.3"
log = "0.4.20"
diff --git a/enostr/src/lib.rs b/enostr/src/lib.rs
@@ -3,6 +3,7 @@ mod error;
mod event;
mod filter;
mod keypair;
+mod note;
mod profile;
mod pubkey;
mod relay;
@@ -14,6 +15,7 @@ pub use ewebsock;
pub use filter::Filter;
pub use keypair::{FullKeypair, Keypair};
pub use nostr::SecretKey;
+pub use note::NoteId;
pub use profile::Profile;
pub use pubkey::Pubkey;
pub use relay::message::{RelayEvent, RelayMessage};
diff --git a/enostr/src/note.rs b/enostr/src/note.rs
@@ -1,87 +1,12 @@
-use crate::Event;
-use shatter::shard::Shards;
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub struct NoteId([u8; 32]);
-#[derive(Debug, Eq, PartialEq)]
-struct RefId(i32);
-
-struct Ref<'a> {
- ref_tag: u8,
- relay_id: Option<&'a str>,
- id: &'a str,
-}
-
-impl<'a> RefId {
- fn get_ref(self, tags: &'a Vec<Vec<String>>) -> Option<Ref<'a>> {
- let ind = self.0 as usize;
-
- if ind > tags.len() - 1 {
- return None;
- }
-
- let tag = &tags[ind];
-
- if tag.len() < 2 {
- return None;
- }
-
- if tag[0].len() != 1 {
- return None;
- }
-
- let ref_tag = if let Some(rtag) = tag[0].as_bytes().first() {
- *rtag
- } else {
- 0
- };
-
- let id = &tag[1];
- if id.len() != 64 {
- return None;
- }
-
- let relay_id = if tag[2].len() == 0 {
- None
- } else {
- Some(&*tag[2])
- };
-
- Some(Ref {
- ref_tag,
- relay_id,
- id,
- })
+impl NoteId {
+ pub fn new(bytes: [u8; 32]) -> Self {
+ NoteId(bytes)
}
-}
-enum MentionType {
- Pubkey,
- Event,
-}
-
-struct Mention {
- index: Option<i32>,
- typ: MentionType,
- refid: RefId,
-}
-
-enum EventRef {
- Mention(Mention),
- ThreadId(RefId),
- Reply(RefId),
- ReplyToRoot(RefId),
-}
-
-struct EventRefs {
- refs: Vec<EventRef>,
-}
-
-struct DM {
- decrypted: Option<String>,
- shards: Shards,
-}
-
-struct Note {
- event: NostrEvent,
- shards: Shards,
- refs: EventRef,
+ pub fn bytes(&self) -> &[u8; 32] {
+ &self.0
+ }
}
diff --git a/src/app.rs b/src/app.rs
@@ -5,13 +5,17 @@ use crate::error::Error;
use crate::frame_history::FrameHistory;
use crate::imgcache::ImageCache;
use crate::notecache::{CachedNote, NoteCache};
+use crate::relay_pool_manager::RelayPoolManager;
use crate::route::Route;
use crate::timeline;
use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter};
use crate::ui;
-use crate::ui::profile::SimpleProfilePreviewController;
-use crate::ui::DesktopSidePanel;
+use crate::ui::{DesktopSidePanel, RelayView, SidePanelAction, View};
use crate::Result;
+use egui_nav::{Nav, NavAction};
+use enostr::RelayPool;
+use std::cell::RefCell;
+use std::rc::Rc;
use egui::{Context, Frame, Style};
use egui_extras::{Size, StripBuilder};
@@ -25,8 +29,6 @@ use std::path::Path;
use std::time::Duration;
use tracing::{debug, error, info, warn};
-use enostr::RelayPool;
-
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum DamusState {
Initializing,
@@ -42,7 +44,7 @@ pub struct Damus {
is_mobile: bool,
/// global navigation for account management popups, etc.
- _nav: Vec<Route>,
+ //nav: Vec<Route>,
pub textmode: bool,
pub timelines: Vec<Timeline>,
@@ -354,7 +356,9 @@ fn poll_notes_for_timeline<'a>(
};
let new_note_ids = damus.ndb.poll_for_notes(sub, 100);
- if !new_note_ids.is_empty() {
+ if new_note_ids.is_empty() {
+ return Ok(());
+ } else {
debug!("{} new notes! {:?}", new_note_ids.len(), new_note_ids);
}
@@ -416,6 +420,10 @@ fn insert_notes_into_timeline(
let timeline = &mut app.timelines[timeline_ind];
let num_prev_items = timeline.notes(filter).len();
let (notes, merge_kind) = timeline::merge_sorted_vecs(timeline.notes(filter), new_refs);
+ debug!(
+ "got merge kind {:?} for {:?} on timeline {}",
+ merge_kind, filter, timeline_ind
+ );
timeline.view_mut(filter).notes = notes;
let new_items = timeline.notes(filter).len() - num_prev_items;
@@ -697,7 +705,6 @@ impl Damus {
img_cache: ImageCache::new(imgcache_dir),
note_cache: NoteCache::default(),
selected_timeline: 0,
- _nav: Vec::with_capacity(6),
timelines,
textmode: false,
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
@@ -730,7 +737,6 @@ impl Damus {
note_cache: NoteCache::default(),
selected_timeline: 0,
timelines,
- _nav: vec![],
textmode: false,
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
account_manager: AccountManager::new(None, crate::key_storage::KeyStorage::None),
@@ -862,14 +868,71 @@ fn render_panel(ctx: &egui::Context, app: &mut Damus, timeline_ind: usize) {
});
}
+fn render_nav(routes: Vec<Route>, timeline_ind: usize, app: &mut Damus, ui: &mut egui::Ui) {
+ let app_ctx = Rc::new(RefCell::new(app));
+
+ let nav_response = Nav::new(routes).show(ui, |ui, nav| match nav.top() {
+ Route::Timeline(_n) => {
+ timeline::timeline_view(ui, &mut app_ctx.borrow_mut(), timeline_ind);
+ }
+
+ Route::ManageAccount => {
+ ui.label("account management view");
+ }
+
+ Route::Thread(_key) => {
+ ui.label("thread view");
+ }
+
+ Route::Relays => {
+ let pool = &mut app_ctx.borrow_mut().pool;
+ let manager = RelayPoolManager::new(pool);
+ RelayView::new(manager).ui(ui);
+ }
+
+ Route::Reply(id) => {
+ let app = app_ctx.borrow();
+ 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;
+ };
+
+ ui.label(format!(
+ "Replying to note by {}",
+ app.ndb
+ .get_profile_by_pubkey(&txn, note.pubkey())
+ .as_ref()
+ .ok()
+ .and_then(|pr| Some(crate::profile::get_profile_name(pr)?.username()))
+ .unwrap_or("??")
+ ));
+ }
+ });
+
+ if let Some(NavAction::Returned) = nav_response.action {
+ app_ctx.borrow_mut().timelines[timeline_ind].routes.pop();
+ }
+}
+
fn render_damus_mobile(ctx: &egui::Context, app: &mut Damus) {
//render_panel(ctx, app, 0);
#[cfg(feature = "profiling")]
puffin::profile_function!();
+ //let routes = app.timelines[0].routes.clone();
+
main_panel(&ctx.style(), app.is_mobile()).show(ctx, |ui| {
- timeline::timeline_view(ui, app, 0);
+ render_nav(app.timelines[0].routes.clone(), 0, app, ui);
});
}
@@ -921,21 +984,27 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, timelines: us
.clip(true)
.horizontal(|mut strip| {
strip.cell(|ui| {
- let side_panel = DesktopSidePanel::new(
- app.account_manager
- .get_selected_account()
- .map(|a| a.pubkey.bytes()),
- SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
- )
- .show(ui);
+ let side_panel = DesktopSidePanel::new(app).show(ui);
if side_panel.response.clicked() {
info!("clicked {:?}", side_panel.action);
+ if let SidePanelAction::Account = side_panel.action {
+ app.timelines[0].routes.push(Route::ManageAccount);
+ }
}
});
for timeline_ind in 0..timelines {
- strip.cell(|ui| timeline::timeline_view(ui, app, timeline_ind));
+ strip.cell(|ui| {
+ render_nav(
+ app.timelines[timeline_ind].routes.clone(),
+ timeline_ind,
+ app,
+ ui,
+ );
+ });
+
+ //strip.cell(|ui| timeline::timeline_view(ui, app, timeline_ind));
}
});
}
diff --git a/src/route.rs b/src/route.rs
@@ -1,9 +1,24 @@
-//use nostrdb::NoteKey;
+use enostr::NoteId;
+use std::fmt;
/// App routing. These describe different places you can go inside Notedeck.
+#[derive(Clone, Debug)]
pub enum Route {
- /*
+ Timeline(String),
ManageAccount,
- Thread(NoteKey),
- */
+ Thread(NoteId),
+ Reply(NoteId),
+ Relays,
+}
+
+impl fmt::Display for Route {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Route::ManageAccount => write!(f, "Manage Account"),
+ Route::Timeline(name) => write!(f, "{}", name),
+ Route::Thread(_id) => write!(f, "Thread"),
+ Route::Reply(_id) => write!(f, "Reply"),
+ Route::Relays => write!(f, "Relays"),
+ }
+ }
}
diff --git a/src/test_data.rs b/src/test_data.rs
@@ -1,13 +1,9 @@
use std::path::Path;
use enostr::{FullKeypair, Pubkey, RelayPool};
-use nostrdb::{Config, Ndb, ProfileRecord};
+use nostrdb::ProfileRecord;
-use crate::{
- account_manager::{AccountManager, UserAccount},
- imgcache::ImageCache,
- key_storage::KeyStorage,
-};
+use crate::{account_manager::UserAccount, Damus};
#[allow(unused_must_use)]
pub fn sample_pool() -> RelayPool {
@@ -87,20 +83,15 @@ pub fn get_test_accounts() -> Vec<UserAccount> {
.collect()
}
-pub fn get_accmgr_and_ndb_and_imgcache() -> (AccountManager, Ndb, ImageCache) {
- let mut account_manager = AccountManager::new(None, KeyStorage::None);
+pub fn get_account_manager_test_app(is_mobile: bool) -> Damus {
+ let db_dir = Path::new(".");
+ let path = db_dir.to_str().unwrap();
+ let mut app = Damus::mock(path, is_mobile);
+
let accounts = get_test_accounts();
for account in accounts {
- account_manager.add_account(account);
+ app.account_manager.add_account(account);
}
- let mut config = Config::new();
- config.set_ingester_threads(2);
-
- let db_dir = Path::new(".");
- let path = db_dir.to_str().unwrap();
- let ndb = Ndb::new(path, &config).expect("ndb");
- let imgcache_dir = db_dir.join("cache/img");
- let img_cache = ImageCache::new(imgcache_dir);
- (account_manager, ndb, img_cache)
+ app
}
diff --git a/src/timeline.rs b/src/timeline.rs
@@ -1,11 +1,14 @@
use crate::notecache::CachedNote;
use crate::{ui, Damus};
+use crate::route::Route;
use egui::containers::scroll_area::ScrollBarVisibility;
use egui::{Direction, Layout};
+
+use crate::ui::BarAction;
use egui_tabs::TabColor;
use egui_virtual_list::VirtualList;
-use enostr::Filter;
+use enostr::{Filter, NoteId};
use nostrdb::{Note, NoteKey, Subscription, Transaction};
use std::cell::RefCell;
use std::cmp::Ordering;
@@ -35,7 +38,7 @@ impl PartialOrd for NoteRef {
}
}
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum ViewFilter {
Notes,
NotesAndReplies,
@@ -122,6 +125,7 @@ pub struct Timeline {
pub filter: Vec<Filter>,
pub views: Vec<TimelineView>,
pub selected_view: i32,
+ pub routes: Vec<Route>,
/// Our nostrdb subscription
pub subscription: Option<Subscription>,
@@ -134,12 +138,14 @@ impl Timeline {
let replies = TimelineView::new(ViewFilter::NotesAndReplies);
let views = vec![notes, replies];
let selected_view = 0;
+ let routes = vec![Route::Timeline("Timeline".to_string())];
Timeline {
filter,
views,
subscription,
selected_view,
+ routes,
}
}
@@ -233,7 +239,7 @@ fn tabs_ui(timeline: &mut Timeline, ui: &mut egui::Ui) {
let stroke = egui::Stroke {
color: ui.visuals().hyperlink_color,
- width: 3.0,
+ width: 2.0,
};
let speed = 0.1f32;
@@ -298,8 +304,19 @@ pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
ui::padding(8.0, ui, |ui| {
let textmode = app.textmode;
- ui.add(ui::Note::new(app, ¬e).note_previews(!textmode));
+ let resp = ui::Note::new(app, ¬e).note_previews(!textmode).show(ui);
+ if let Some(action) = resp.action {
+ debug!("bar action: {:?}", action);
+ match action {
+ BarAction::Reply => {
+ app.timelines[timeline]
+ .routes
+ .push(Route::Reply(NoteId::new(note.id().to_owned())));
+ }
+ }
+ }
});
+
ui::hline(ui);
//ui.add(egui::Separator::default().spacing(0.0));
@@ -308,6 +325,7 @@ pub fn timeline_view(ui: &mut egui::Ui, app: &mut Damus, timeline: usize) {
});
}
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum MergeKind {
FrontInsert,
Spliced,
diff --git a/src/ui/account_management.rs b/src/ui/account_management.rs
@@ -2,103 +2,69 @@ use crate::colors::PINK;
use crate::{
account_manager::AccountManager,
app_style::NotedeckTextStyle,
- ui::{Preview, PreviewConfig, View},
+ ui::{profile_preview_controller, Preview, PreviewConfig, View},
+ Damus,
};
use egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2};
use super::profile::preview::SimpleProfilePreview;
-use super::profile::{ProfilePreviewOp, SimpleProfilePreviewController};
+use super::profile::ProfilePreviewOp;
-pub struct AccountManagementView<'a> {
- mobile: bool,
- account_manager: &'a mut AccountManager,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
-}
+pub struct AccountManagementView {}
-impl<'a> View for AccountManagementView<'a> {
- fn ui(&mut self, ui: &mut egui::Ui) {
- if self.mobile {
- self.show_mobile(ui);
- } else {
- self.show(ui);
- }
- }
-}
-
-impl<'a> AccountManagementView<'a> {
- pub fn new(
- mobile: bool,
- account_manager: &'a mut AccountManager,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
- ) -> Self {
- AccountManagementView {
- mobile,
- account_manager,
- simple_preview_controller,
- }
- }
-
- fn show(&mut self, ui: &mut egui::Ui) {
+impl AccountManagementView {
+ fn show(app: &mut Damus, ui: &mut egui::Ui) {
Frame::none().outer_margin(24.0).show(ui, |ui| {
- self.top_section_buttons_widget(ui);
+ Self::top_section_buttons_widget(ui);
ui.add_space(8.0);
scroll_area().show(ui, |ui| {
- self.show_accounts(ui);
+ Self::show_accounts(app, ui);
});
});
}
- fn show_accounts(&mut self, ui: &mut egui::Ui) {
- let maybe_remove = self.simple_preview_controller.set_profile_previews(
- self.account_manager,
- ui,
- account_card_ui(),
- );
+ fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) {
+ let maybe_remove =
+ profile_preview_controller::set_profile_previews(app, ui, account_card_ui());
- self.maybe_remove_accounts(maybe_remove);
+ Self::maybe_remove_accounts(&mut app.account_manager, maybe_remove);
}
- fn show_accounts_mobile(&mut self, ui: &mut egui::Ui) {
+ fn show_accounts_mobile(app: &mut Damus, ui: &mut egui::Ui) {
ui.allocate_ui_with_layout(
Vec2::new(ui.available_size_before_wrap().x, 32.0),
Layout::top_down(egui::Align::Min),
|ui| {
// create all account 'cards' and get the indicies the user requested to remove
- let maybe_remove = self.simple_preview_controller.set_profile_previews(
- self.account_manager,
+ let maybe_remove = profile_preview_controller::set_profile_previews(
+ app,
ui,
account_card_ui(), // closure for creating an account 'card'
);
// remove all account indicies user requested
- self.maybe_remove_accounts(maybe_remove);
+ Self::maybe_remove_accounts(&mut app.account_manager, maybe_remove);
},
);
}
- fn maybe_remove_accounts(&mut self, account_indices: Option<Vec<usize>>) {
+ fn maybe_remove_accounts(manager: &mut AccountManager, account_indices: Option<Vec<usize>>) {
if let Some(to_remove) = account_indices {
to_remove
.iter()
- .for_each(|index| self.account_manager.remove_account(*index));
+ .for_each(|index| manager.remove_account(*index));
}
}
- fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
- egui::CentralPanel::default()
- .show(ui.ctx(), |ui| {
- mobile_title(ui);
- self.top_section_buttons_widget(ui);
+ fn show_mobile(app: &mut Damus, ui: &mut egui::Ui) {
+ mobile_title(ui);
+ Self::top_section_buttons_widget(ui);
- ui.add_space(8.0);
- scroll_area().show(ui, |ui| {
- self.show_accounts_mobile(ui);
- });
- })
- .response
+ ui.add_space(8.0);
+ scroll_area().show(ui, |ui| Self::show_accounts_mobile(app, ui));
}
- fn top_section_buttons_widget(&mut self, ui: &mut egui::Ui) -> egui::Response {
+ fn top_section_buttons_widget(ui: &mut egui::Ui) -> egui::Response {
ui.horizontal(|ui| {
ui.allocate_ui_with_layout(
Vec2::new(ui.available_size_before_wrap().x, 32.0),
@@ -233,44 +199,36 @@ fn selected_widget() -> impl egui::Widget {
// PREVIEWS
mod preview {
- use nostrdb::Ndb;
+
use super::*;
- use crate::{imgcache::ImageCache, test_data::get_accmgr_and_ndb_and_imgcache};
+ use crate::test_data::get_account_manager_test_app;
pub struct AccountManagementPreview {
is_mobile: bool,
- account_manager: AccountManager,
- ndb: Ndb,
- img_cache: ImageCache,
+ app: Damus,
}
impl AccountManagementPreview {
fn new(is_mobile: bool) -> Self {
- let (account_manager, ndb, img_cache) = get_accmgr_and_ndb_and_imgcache();
+ let app = get_account_manager_test_app(is_mobile);
- AccountManagementPreview {
- is_mobile,
- account_manager,
- ndb,
- img_cache,
- }
+ AccountManagementPreview { is_mobile, app }
}
}
impl View for AccountManagementPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
ui.add_space(24.0);
- AccountManagementView::new(
- self.is_mobile,
- &mut self.account_manager,
- SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
- )
- .ui(ui);
+ if self.is_mobile {
+ AccountManagementView::show_mobile(&mut self.app, ui);
+ } else {
+ AccountManagementView::show(&mut self.app, ui);
+ }
}
}
- impl<'a> Preview for AccountManagementView<'a> {
+ impl Preview for AccountManagementView {
type Prev = AccountManagementPreview;
fn preview(cfg: PreviewConfig) -> Self::Prev {
diff --git a/src/ui/account_switcher.rs b/src/ui/account_switcher.rs
@@ -1,21 +1,18 @@
use crate::{
- account_manager::{AccountManager, UserAccount},
- colors::PINK,
- profile::DisplayName,
- Result,
+ account_manager::UserAccount, colors::PINK, profile::DisplayName,
+ ui::profile_preview_controller, Damus, Result,
};
+
+use nostrdb::Ndb;
+
use egui::{
Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea,
Sense, Vec2,
};
-use super::profile::{preview::SimpleProfilePreview, SimpleProfilePreviewController};
+use super::profile::preview::SimpleProfilePreview;
-pub struct AccountSelectionWidget<'a> {
- is_mobile: bool,
- account_manager: &'a AccountManager,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
-}
+pub struct AccountSelectionWidget {}
enum AccountSelectAction {
RemoveAccount { _index: usize },
@@ -28,36 +25,24 @@ struct AccountSelectResponse {
action: Option<AccountSelectAction>,
}
-impl<'a> AccountSelectionWidget<'a> {
- pub fn new(
- is_mobile: bool,
- account_manager: &'a AccountManager,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
- ) -> Self {
- AccountSelectionWidget {
- is_mobile,
- account_manager,
- simple_preview_controller,
- }
- }
-
- pub fn ui(&'a mut self, ui: &mut egui::Ui) {
- if self.is_mobile {
- self.show_mobile(ui);
+impl AccountSelectionWidget {
+ pub fn ui(app: &mut Damus, ui: &mut egui::Ui) {
+ if app.is_mobile() {
+ Self::show_mobile(ui);
} else {
- self.show(ui);
+ Self::show(app, ui);
}
}
- fn show(&mut self, ui: &mut egui::Ui) -> AccountSelectResponse {
+ fn show(app: &mut Damus, ui: &mut egui::Ui) -> AccountSelectResponse {
let mut res = AccountSelectResponse::default();
- let mut selected_index = self.account_manager.get_selected_account_index();
+ let mut selected_index = app.account_manager.get_selected_account_index();
Frame::none().outer_margin(8.0).show(ui, |ui| {
res = top_section_widget(ui);
scroll_area().show(ui, |ui| {
- if let Some(_index) = self.show_accounts(ui) {
+ if let Some(_index) = Self::show_accounts(app, ui) {
selected_index = Some(_index);
res.action = Some(AccountSelectAction::SelectAccount { _index });
}
@@ -66,9 +51,9 @@ impl<'a> AccountSelectionWidget<'a> {
ui.add(add_account_button());
if let Some(_index) = selected_index {
- if let Some(account) = self.account_manager.get_account(_index) {
+ if let Some(account) = app.account_manager.get_account(_index) {
ui.add_space(8.0);
- if self.handle_sign_out(ui, account) {
+ if Self::handle_sign_out(&app.ndb, ui, account) {
res.action = Some(AccountSelectAction::RemoveAccount { _index })
}
}
@@ -80,29 +65,30 @@ impl<'a> AccountSelectionWidget<'a> {
res
}
- fn handle_sign_out(&mut self, ui: &mut egui::Ui, account: &UserAccount) -> bool {
- if let Ok(response) = self.sign_out_button(ui, account) {
+ fn handle_sign_out(ndb: &Ndb, ui: &mut egui::Ui, account: &UserAccount) -> bool {
+ if let Ok(response) = Self::sign_out_button(ndb, ui, account) {
response.clicked()
} else {
false
}
}
- fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
+ fn show_mobile(ui: &mut egui::Ui) -> egui::Response {
let _ = ui;
todo!()
}
- fn show_accounts(&mut self, ui: &mut egui::Ui) -> Option<usize> {
- self.simple_preview_controller.view_profile_previews(
- self.account_manager,
- ui,
- account_switcher_card_ui(),
- )
+ fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) -> Option<usize> {
+ profile_preview_controller::view_profile_previews(app, ui, account_switcher_card_ui)
}
- fn sign_out_button(&self, ui: &mut egui::Ui, account: &UserAccount) -> Result<egui::Response> {
- self.simple_preview_controller.show_with_nickname(
+ fn sign_out_button(
+ ndb: &Ndb,
+ ui: &mut egui::Ui,
+ account: &UserAccount,
+ ) -> Result<egui::Response> {
+ profile_preview_controller::show_with_nickname(
+ ndb,
ui,
account.pubkey.bytes(),
|ui: &mut egui::Ui, username: &DisplayName| {
@@ -122,42 +108,40 @@ impl<'a> AccountSelectionWidget<'a> {
}
}
-fn account_switcher_card_ui() -> fn(
+fn account_switcher_card_ui(
ui: &mut egui::Ui,
preview: SimpleProfilePreview,
width: f32,
is_selected: bool,
index: usize,
) -> bool {
- |ui, preview, width, is_selected, index| {
- let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| {
- Frame::none()
- .show(ui, |ui| {
- ui.add_space(8.0);
- ui.horizontal(|ui| {
- if is_selected {
- Frame::none()
- .rounding(Rounding::same(8.0))
- .inner_margin(Margin::same(8.0))
- .fill(Color32::from_rgb(0x45, 0x1B, 0x59))
- .show(ui, |ui| {
- ui.add(preview);
- ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
- ui.add(selection_widget());
- });
+ let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| {
+ Frame::none()
+ .show(ui, |ui| {
+ ui.add_space(8.0);
+ ui.horizontal(|ui| {
+ if is_selected {
+ Frame::none()
+ .rounding(Rounding::same(8.0))
+ .inner_margin(Margin::same(8.0))
+ .fill(Color32::from_rgb(0x45, 0x1B, 0x59))
+ .show(ui, |ui| {
+ ui.add(preview);
+ ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
+ ui.add(selection_widget());
});
- } else {
- ui.add_space(8.0);
- ui.add(preview);
- }
- });
- })
- .response
- });
+ });
+ } else {
+ ui.add_space(8.0);
+ ui.add(preview);
+ }
+ });
+ })
+ .response
+ });
- ui.interact(resp.rect, Id::new(index), Sense::click())
- .clicked()
- }
+ ui.interact(resp.rect, Id::new(index), Sense::click())
+ .clicked()
}
fn selection_widget() -> impl egui::Widget {
@@ -215,48 +199,33 @@ fn add_account_button() -> egui::Button<'static> {
}
mod previews {
- use nostrdb::Ndb;
use crate::{
- account_manager::AccountManager,
- imgcache::ImageCache,
test_data,
- ui::{profile::SimpleProfilePreviewController, Preview, PreviewConfig, View},
+ ui::{Preview, PreviewConfig, View},
+ Damus,
};
use super::AccountSelectionWidget;
pub struct AccountSelectionPreview {
- is_mobile: bool,
- account_manager: AccountManager,
- ndb: Ndb,
- img_cache: ImageCache,
+ app: Damus,
}
impl AccountSelectionPreview {
fn new(is_mobile: bool) -> Self {
- let (account_manager, ndb, img_cache) = test_data::get_accmgr_and_ndb_and_imgcache();
- AccountSelectionPreview {
- is_mobile,
- account_manager,
- ndb,
- img_cache,
- }
+ let app = test_data::get_account_manager_test_app(is_mobile);
+ AccountSelectionPreview { app }
}
}
impl View for AccountSelectionPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
- AccountSelectionWidget::new(
- self.is_mobile,
- &self.account_manager,
- SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
- )
- .ui(ui);
+ AccountSelectionWidget::show(&mut self.app, ui);
}
}
- impl<'a> Preview for AccountSelectionWidget<'a> {
+ impl Preview for AccountSelectionWidget {
type Prev = AccountSelectionPreview;
fn preview(cfg: PreviewConfig) -> Self::Prev {
diff --git a/src/ui/mod.rs b/src/ui/mod.rs
@@ -13,11 +13,11 @@ pub mod username;
pub use account_management::AccountManagementView;
pub use account_switcher::AccountSelectionWidget;
pub use mention::Mention;
-pub use note::Note;
+pub use note::{BarAction, Note, NoteResponse};
pub use preview::{Preview, PreviewApp, PreviewConfig};
-pub use profile::{ProfilePic, ProfilePreview};
+pub use profile::{profile_preview_controller, ProfilePic, ProfilePreview};
pub use relay::RelayView;
-pub use side_panel::DesktopSidePanel;
+pub use side_panel::{DesktopSidePanel, SidePanelAction};
pub use username::Username;
use egui::Margin;
diff --git a/src/ui/note/mod.rs b/src/ui/note/mod.rs
@@ -15,12 +15,17 @@ pub struct Note<'a> {
flags: NoteOptions,
}
+pub struct NoteResponse {
+ pub response: egui::Response,
+ pub action: Option<BarAction>,
+}
+
impl<'a> egui::Widget for Note<'a> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
if self.app.textmode {
self.textmode_ui(ui)
} else {
- self.standard_ui(ui)
+ self.show(ui).response
}
}
}
@@ -186,103 +191,115 @@ impl<'a> Note<'a> {
.response
}
- pub fn standard_ui(self, ui: &mut egui::Ui) -> egui::Response {
+ pub fn show(self, ui: &mut egui::Ui) -> NoteResponse {
#[cfg(feature = "profiling")]
puffin::profile_function!();
let note_key = self.note.key().expect("todo: support non-db notes");
let txn = self.note.txn().expect("todo: support non-db notes");
+ let mut note_action: Option<BarAction> = None;
+
+ let response = ui
+ .with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
+ ui.spacing_mut().item_spacing.x = 16.0;
+
+ let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey());
+
+ match profile
+ .as_ref()
+ .ok()
+ .and_then(|p| p.record().profile()?.picture())
+ {
+ // these have different lifetimes and types,
+ // so the calls must be separate
+ Some(pic) => {
+ let expand_size = 5.0;
+ let anim_speed = 0.05;
+ let profile_key = profile.as_ref().unwrap().record().note_key();
+ let note_key = note_key.as_u64();
+
+ if self.app.is_mobile() {
+ ui.add(ui::ProfilePic::new(&mut self.app.img_cache, pic));
+ } else {
+ let (rect, size) = ui::anim::hover_expand(
+ ui,
+ egui::Id::new(ProfileAnimId {
+ profile_key,
+ note_key,
+ }),
+ ui::ProfilePic::default_size(),
+ expand_size,
+ anim_speed,
+ );
+
+ ui.put(
+ rect,
+ ui::ProfilePic::new(&mut self.app.img_cache, pic).size(size),
+ )
+ .on_hover_ui_at_pointer(|ui| {
+ ui.set_max_width(300.0);
+ ui.add(ui::ProfilePreview::new(
+ profile.as_ref().unwrap(),
+ &mut self.app.img_cache,
+ ));
+ });
+ }
+ }
+ None => {
+ ui.add(ui::ProfilePic::new(
+ &mut self.app.img_cache,
+ ui::ProfilePic::no_pfp_url(),
+ ));
+ }
+ }
- ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
- ui.spacing_mut().item_spacing.x = 16.0;
-
- let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey());
-
- match profile
- .as_ref()
- .ok()
- .and_then(|p| p.record().profile()?.picture())
- {
- // these have different lifetimes and types,
- // so the calls must be separate
- Some(pic) => {
- let expand_size = 5.0;
- let anim_speed = 0.05;
- let profile_key = profile.as_ref().unwrap().record().note_key();
- let note_key = note_key.as_u64();
-
- if self.app.is_mobile() {
- ui.add(ui::ProfilePic::new(&mut self.app.img_cache, pic));
- } else {
- let (rect, size) = ui::anim::hover_expand(
- ui,
- egui::Id::new(ProfileAnimId {
- profile_key,
- note_key,
- }),
- ui::ProfilePic::default_size(),
- expand_size,
- anim_speed,
+ ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
+ ui.horizontal(|ui| {
+ ui.spacing_mut().item_spacing.x = 2.0;
+ ui.add(
+ ui::Username::new(profile.as_ref().ok(), self.note.pubkey())
+ .abbreviated(20),
);
- ui.put(
- rect,
- ui::ProfilePic::new(&mut self.app.img_cache, pic).size(size),
- )
- .on_hover_ui_at_pointer(|ui| {
- ui.set_max_width(300.0);
- ui.add(ui::ProfilePreview::new(
- profile.as_ref().unwrap(),
- &mut self.app.img_cache,
- ));
- });
- }
- }
- None => {
- ui.add(ui::ProfilePic::new(
- &mut self.app.img_cache,
- ui::ProfilePic::no_pfp_url(),
+ let cached_note = self
+ .app
+ .note_cache_mut()
+ .cached_note_or_insert_mut(note_key, self.note);
+ render_reltime(ui, cached_note, true);
+ });
+
+ ui.horizontal(|ui| {
+ ui.spacing_mut().item_spacing.x = 2.0;
+ reply_desc(ui, txn, self.app, note_key, self.note);
+ });
+
+ ui.add(NoteContents::new(
+ self.app,
+ txn,
+ self.note,
+ note_key,
+ self.options(),
));
- }
- }
-
- ui.with_layout(egui::Layout::top_down(egui::Align::LEFT), |ui| {
- ui.horizontal(|ui| {
- ui.spacing_mut().item_spacing.x = 2.0;
- ui.add(
- ui::Username::new(profile.as_ref().ok(), self.note.pubkey())
- .abbreviated(20),
- );
-
- let cached_note = self
- .app
- .note_cache_mut()
- .cached_note_or_insert_mut(note_key, self.note);
- render_reltime(ui, cached_note, true);
- });
- ui.horizontal(|ui| {
- ui.spacing_mut().item_spacing.x = 2.0;
- reply_desc(ui, txn, self.app, note_key, self.note);
+ if self.options().has_actionbar() {
+ note_action = render_note_actionbar(ui).inner;
+ }
});
+ })
+ .response;
- ui.add(NoteContents::new(
- self.app,
- txn,
- self.note,
- note_key,
- self.options(),
- ));
-
- if self.options().has_actionbar() {
- render_note_actionbar(ui);
- }
- });
- })
- .response
+ NoteResponse {
+ response,
+ action: note_action,
+ }
}
}
-fn render_note_actionbar(ui: &mut egui::Ui) -> egui::InnerResponse<()> {
+#[derive(Debug, Eq, PartialEq, Copy, Clone)]
+pub enum BarAction {
+ Reply,
+}
+
+fn render_note_actionbar(ui: &mut egui::Ui) -> egui::InnerResponse<Option<BarAction>> {
ui.horizontal(|ui| {
let img_data = if ui.style().visuals.dark_mode {
egui::include_image!("../../../assets/icons/reply.png")
@@ -299,9 +316,11 @@ fn render_note_actionbar(ui: &mut egui::Ui) -> egui::InnerResponse<()> {
.fill(ui.style().visuals.panel_fill),
)
.clicked()
- {}
-
- //if ui.add(egui::Button::new("like")).clicked() {}
+ {
+ Some(BarAction::Reply)
+ } else {
+ None
+ }
})
}
diff --git a/src/ui/profile/mod.rs b/src/ui/profile/mod.rs
@@ -1,7 +1,7 @@
pub mod picture;
pub mod preview;
-mod profile_preview_controller;
+pub mod profile_preview_controller;
pub use picture::ProfilePic;
pub use preview::ProfilePreview;
-pub use profile_preview_controller::{ProfilePreviewOp, SimpleProfilePreviewController};
+pub use profile_preview_controller::ProfilePreviewOp;
diff --git a/src/ui/profile/profile_preview_controller.rs b/src/ui/profile/profile_preview_controller.rs
@@ -1,168 +1,155 @@
use nostrdb::{Ndb, Transaction};
-use crate::{account_manager::AccountManager, imgcache::ImageCache, DisplayName, Result};
+use crate::{Damus, DisplayName, Result};
use super::{
preview::{get_display_name, get_profile_url, SimpleProfilePreview},
ProfilePic,
};
-pub struct SimpleProfilePreviewController<'a> {
- ndb: &'a Ndb,
- img_cache: &'a mut ImageCache,
-}
-
#[derive(Debug)]
pub enum ProfilePreviewOp {
RemoveAccount,
SwitchTo,
}
-impl<'a> SimpleProfilePreviewController<'a> {
- pub fn new(ndb: &'a Ndb, img_cache: &'a mut ImageCache) -> Self {
- SimpleProfilePreviewController { ndb, img_cache }
- }
-
- pub fn set_profile_previews(
- &mut self,
- account_manager: &mut AccountManager,
+pub fn set_profile_previews(
+ app: &mut Damus,
+ ui: &mut egui::Ui,
+ add_preview_ui: fn(
ui: &mut egui::Ui,
- add_preview_ui: fn(
- ui: &mut egui::Ui,
- preview: SimpleProfilePreview,
- width: f32,
- is_selected: bool,
- ) -> Option<ProfilePreviewOp>,
- ) -> Option<Vec<usize>> {
- let mut to_remove: Option<Vec<usize>> = None;
-
- let width = ui.available_width();
-
- let txn = if let Ok(txn) = Transaction::new(self.ndb) {
- txn
+ preview: SimpleProfilePreview,
+ width: f32,
+ is_selected: bool,
+ ) -> Option<ProfilePreviewOp>,
+) -> Option<Vec<usize>> {
+ let mut to_remove: Option<Vec<usize>> = None;
+
+ let width = ui.available_width();
+
+ let txn = if let Ok(txn) = Transaction::new(&app.ndb) {
+ txn
+ } else {
+ return None;
+ };
+
+ for i in 0..app.account_manager.num_accounts() {
+ let account = if let Some(account) = app.account_manager.get_account(i) {
+ account
} else {
- return None;
+ continue;
};
- for i in 0..account_manager.num_accounts() {
- let account = if let Some(account) = account_manager.get_account(i) {
- account
+ let profile =
+ if let Ok(profile) = app.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
+ profile
} else {
continue;
};
- let profile =
- if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
- profile
- } else {
- continue;
- };
+ let preview = SimpleProfilePreview::new(&profile, &mut app.img_cache);
- let preview = SimpleProfilePreview::new(&profile, self.img_cache);
-
- let is_selected = if let Some(selected) = account_manager.get_selected_account_index() {
- i == selected
- } else {
- false
- };
+ let is_selected = if let Some(selected) = app.account_manager.get_selected_account_index() {
+ i == selected
+ } else {
+ false
+ };
- let op = if let Some(op) = add_preview_ui(ui, preview, width, is_selected) {
- op
- } else {
- continue;
- };
+ let op = if let Some(op) = add_preview_ui(ui, preview, width, is_selected) {
+ op
+ } else {
+ continue;
+ };
- match op {
- ProfilePreviewOp::RemoveAccount => {
- if to_remove.is_none() {
- to_remove = Some(Vec::new());
- }
- to_remove.as_mut().unwrap().push(i);
+ match op {
+ ProfilePreviewOp::RemoveAccount => {
+ if to_remove.is_none() {
+ to_remove = Some(Vec::new());
}
- ProfilePreviewOp::SwitchTo => account_manager.select_account(i),
+ to_remove.as_mut().unwrap().push(i);
}
+ ProfilePreviewOp::SwitchTo => app.account_manager.select_account(i),
}
-
- to_remove
}
- pub fn view_profile_previews(
- &mut self,
- account_manager: &AccountManager,
+ to_remove
+}
+
+pub fn view_profile_previews(
+ app: &mut Damus,
+ ui: &mut egui::Ui,
+ add_preview_ui: fn(
ui: &mut egui::Ui,
- add_preview_ui: fn(
- ui: &mut egui::Ui,
- preview: SimpleProfilePreview,
- width: f32,
- is_selected: bool,
- index: usize,
- ) -> bool,
- ) -> Option<usize> {
- let width = ui.available_width();
-
- let txn = if let Ok(txn) = Transaction::new(self.ndb) {
- txn
+ preview: SimpleProfilePreview,
+ width: f32,
+ is_selected: bool,
+ index: usize,
+ ) -> bool,
+) -> Option<usize> {
+ let width = ui.available_width();
+
+ let txn = if let Ok(txn) = Transaction::new(&app.ndb) {
+ txn
+ } else {
+ return None;
+ };
+
+ for i in 0..app.account_manager.num_accounts() {
+ let account = if let Some(account) = app.account_manager.get_account(i) {
+ account
} else {
- return None;
+ continue;
};
- for i in 0..account_manager.num_accounts() {
- let account = if let Some(account) = account_manager.get_account(i) {
- account
+ let profile =
+ if let Ok(profile) = app.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
+ profile
} else {
continue;
};
- let profile =
- if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
- profile
- } else {
- continue;
- };
-
- let preview = SimpleProfilePreview::new(&profile, self.img_cache);
+ let preview = SimpleProfilePreview::new(&profile, &mut app.img_cache);
- let is_selected = if let Some(selected) = account_manager.get_selected_account_index() {
- i == selected
- } else {
- false
- };
+ let is_selected = if let Some(selected) = app.account_manager.get_selected_account_index() {
+ i == selected
+ } else {
+ false
+ };
- if add_preview_ui(ui, preview, width, is_selected, i) {
- return Some(i);
- }
+ if add_preview_ui(ui, preview, width, is_selected, i) {
+ return Some(i);
}
-
- None
}
- pub fn show_with_nickname(
- &self,
- ui: &mut egui::Ui,
- key: &[u8; 32],
- ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response,
- ) -> Result<egui::Response> {
- let txn = Transaction::new(self.ndb)?;
- let profile = self.ndb.get_profile_by_pubkey(&txn, key)?;
- Ok(ui_element(ui, &get_display_name(&profile)))
- }
+ None
+}
- pub fn show_with_pfp(
- self,
- ui: &mut egui::Ui,
- key: &[u8; 32],
- ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response,
- ) -> Option<egui::Response> {
- if let Ok(txn) = Transaction::new(self.ndb) {
- let profile = self.ndb.get_profile_by_pubkey(&txn, key);
-
- if let Ok(profile) = profile {
- return Some(ui_element(
- ui,
- ProfilePic::new(self.img_cache, get_profile_url(&profile)),
- ));
- }
+pub fn show_with_nickname(
+ ndb: &Ndb,
+ ui: &mut egui::Ui,
+ key: &[u8; 32],
+ ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response,
+) -> Result<egui::Response> {
+ let txn = Transaction::new(ndb)?;
+ let profile = ndb.get_profile_by_pubkey(&txn, key)?;
+ Ok(ui_element(ui, &get_display_name(&profile)))
+}
+
+pub fn show_with_pfp(
+ app: &mut Damus,
+ ui: &mut egui::Ui,
+ key: &[u8; 32],
+ ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response,
+) -> Option<egui::Response> {
+ if let Ok(txn) = Transaction::new(&app.ndb) {
+ let profile = app.ndb.get_profile_by_pubkey(&txn, key);
+
+ if let Ok(profile) = profile {
+ return Some(ui_element(
+ ui,
+ ProfilePic::new(&mut app.img_cache, get_profile_url(&profile)),
+ ));
}
- None
}
+ None
}
diff --git a/src/ui/side_panel.rs b/src/ui/side_panel.rs
@@ -1,12 +1,17 @@
use egui::{Button, Layout, SidePanel, Vec2, Widget};
-use crate::account_manager::AccountManager;
+use crate::{ui::profile_preview_controller, Damus};
-use super::{profile::SimpleProfilePreviewController, ProfilePic, View};
+use super::{ProfilePic, View};
pub struct DesktopSidePanel<'a> {
- selected_account: Option<&'a [u8; 32]>,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
+ app: &'a mut Damus,
+}
+
+impl<'a> View for DesktopSidePanel<'a> {
+ fn ui(&mut self, ui: &mut egui::Ui) {
+ self.show(ui);
+ }
}
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
@@ -28,21 +33,9 @@ impl SidePanelResponse {
}
}
-impl<'a> Widget for DesktopSidePanel<'a> {
- fn ui(self, ui: &mut egui::Ui) -> egui::Response {
- self.show(ui).response
- }
-}
-
impl<'a> DesktopSidePanel<'a> {
- pub fn new(
- selected_account: Option<&'a [u8; 32]>,
- simple_preview_controller: SimpleProfilePreviewController<'a>,
- ) -> Self {
- DesktopSidePanel {
- selected_account,
- simple_preview_controller,
- }
+ pub fn new(app: &'a mut Damus) -> Self {
+ DesktopSidePanel { app }
}
pub fn panel() -> SidePanel {
@@ -51,7 +44,7 @@ impl<'a> DesktopSidePanel<'a> {
.exact_width(40.0)
}
- pub fn show(self, ui: &mut egui::Ui) -> SidePanelResponse {
+ pub fn show(&mut self, ui: &mut egui::Ui) -> SidePanelResponse {
let dark_mode = ui.ctx().style().visuals.dark_mode;
let spacing_amt = 16.0;
@@ -77,12 +70,15 @@ impl<'a> DesktopSidePanel<'a> {
SidePanelResponse::new(inner.inner, inner.response)
}
- fn pfp_button(self, ui: &mut egui::Ui) -> egui::Response {
- if let Some(selected_account) = self.selected_account {
- if let Some(response) =
- self.simple_preview_controller
- .show_with_pfp(ui, selected_account, show_pfp())
- {
+ fn pfp_button(&mut self, ui: &mut egui::Ui) -> egui::Response {
+ let selected_account = self.app.account_manager.get_selected_account();
+ if let Some(selected_account) = selected_account {
+ if let Some(response) = profile_preview_controller::show_with_pfp(
+ self.app,
+ ui,
+ &selected_account.pubkey.bytes().clone(),
+ show_pfp(),
+ ) {
return response;
}
}
@@ -123,10 +119,9 @@ fn add_column_button(dark_mode: bool) -> egui::Button<'static> {
}
mod preview {
- use nostrdb::Ndb;
+
use crate::{
- imgcache::ImageCache,
test_data,
ui::{Preview, PreviewConfig},
};
@@ -134,33 +129,25 @@ mod preview {
use super::*;
pub struct DesktopSidePanelPreview {
- account_manager: AccountManager,
- ndb: Ndb,
- img_cache: ImageCache,
+ app: Damus,
}
impl DesktopSidePanelPreview {
- fn new() -> Self {
- let (account_manager, ndb, img_cache) = test_data::get_accmgr_and_ndb_and_imgcache();
- DesktopSidePanelPreview {
- account_manager,
- ndb,
- img_cache,
- }
+ fn new(is_mobile: bool) -> Self {
+ let app = test_data::get_account_manager_test_app(is_mobile);
+ DesktopSidePanelPreview { app }
}
}
impl View for DesktopSidePanelPreview {
fn ui(&mut self, ui: &mut egui::Ui) {
- let selected_account = self
+ let _selected_account = self
+ .app
.account_manager
.get_selected_account()
.map(|x| x.pubkey.bytes());
- let panel = DesktopSidePanel::new(
- selected_account,
- SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
- );
+ let mut panel = DesktopSidePanel::new(&mut self.app);
DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui));
}
@@ -169,8 +156,8 @@ mod preview {
impl<'a> Preview for DesktopSidePanel<'a> {
type Prev = DesktopSidePanelPreview;
- fn preview(_cfg: PreviewConfig) -> Self::Prev {
- DesktopSidePanelPreview::new()
+ fn preview(cfg: PreviewConfig) -> Self::Prev {
+ DesktopSidePanelPreview::new(cfg.is_mobile)
}
}
}