initial navigation

This commit is contained in:
William Casarin
2024-06-04 01:51:30 -05:00
parent bff0f3f628
commit 0dd33c90e7
16 changed files with 528 additions and 576 deletions

42
Cargo.lock generated
View File

@@ -1035,15 +1035,6 @@ dependencies = [
"serde", "serde",
] ]
[[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]] [[package]]
name = "egui-wgpu" name = "egui-wgpu"
version = "0.27.2" version = "0.27.2"
@@ -1111,6 +1102,25 @@ dependencies = [
"puffin", "puffin",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "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]] [[package]]
@@ -1178,7 +1188,6 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"shatter",
"tracing", "tracing",
] ]
@@ -2454,8 +2463,9 @@ dependencies = [
"console_error_panic_hook", "console_error_panic_hook",
"eframe", "eframe",
"egui", "egui",
"egui-tabs",
"egui_extras", "egui_extras",
"egui_nav",
"egui_tabs",
"egui_virtual_list", "egui_virtual_list",
"ehttp 0.2.0", "ehttp 0.2.0",
"enostr", "enostr",
@@ -3524,16 +3534,6 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[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]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"

View File

@@ -14,12 +14,25 @@ crate-type = ["lib", "cdylib"]
[dependencies] [dependencies]
#egui-android = { git = "https://github.com/jb55/egui-android.git" } #egui-android = { git = "https://github.com/jb55/egui-android.git" }
egui = "0.27.2" 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 = { version = "0.27.2", default-features = false, features = [ "glow", "android-native-activity" ] }
#eframe = "0.22.0" #eframe = "0.22.0"
egui_extras = { version = "0.27.2", features = ["all_loaders"] } egui_extras = { version = "0.27.2", features = ["all_loaders"] }
ehttp = "0.2.0" 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" ] } reqwest = { version = "0.12.4", default-features = false, features = [ "rustls-tls-native-roots" ] }
image = { version = "0.24", features = ["jpeg", "png", "webp"] } image = { version = "0.24", features = ["jpeg", "png", "webp"] }
log = "0.4.17" log = "0.4.17"

View File

@@ -11,7 +11,6 @@ serde_derive = "1"
serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence serde = { version = "1", features = ["derive"] } # You only need this if you want app persistence
serde_json = "1.0.89" serde_json = "1.0.89"
tracing = "0.1.37" tracing = "0.1.37"
shatter = "0.1.1"
nostr = { version = "0.30.0" } nostr = { version = "0.30.0" }
hex = "0.4.3" hex = "0.4.3"
log = "0.4.20" log = "0.4.20"

View File

@@ -3,6 +3,7 @@ mod error;
mod event; mod event;
mod filter; mod filter;
mod keypair; mod keypair;
mod note;
mod profile; mod profile;
mod pubkey; mod pubkey;
mod relay; mod relay;
@@ -14,6 +15,7 @@ pub use ewebsock;
pub use filter::Filter; pub use filter::Filter;
pub use keypair::{FullKeypair, Keypair}; pub use keypair::{FullKeypair, Keypair};
pub use nostr::SecretKey; pub use nostr::SecretKey;
pub use note::NoteId;
pub use profile::Profile; pub use profile::Profile;
pub use pubkey::Pubkey; pub use pubkey::Pubkey;
pub use relay::message::{RelayEvent, RelayMessage}; pub use relay::message::{RelayEvent, RelayMessage};

View File

@@ -1,87 +1,12 @@
use crate::Event; #[derive(Debug, Clone, Copy, Eq, PartialEq)]
use shatter::shard::Shards; pub struct NoteId([u8; 32]);
#[derive(Debug, Eq, PartialEq)] impl NoteId {
struct RefId(i32); pub fn new(bytes: [u8; 32]) -> Self {
NoteId(bytes)
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]; pub fn bytes(&self) -> &[u8; 32] {
&self.0
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,
})
} }
} }
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,
}

View File

@@ -5,13 +5,17 @@ use crate::error::Error;
use crate::frame_history::FrameHistory; use crate::frame_history::FrameHistory;
use crate::imgcache::ImageCache; use crate::imgcache::ImageCache;
use crate::notecache::{CachedNote, NoteCache}; use crate::notecache::{CachedNote, NoteCache};
use crate::relay_pool_manager::RelayPoolManager;
use crate::route::Route; use crate::route::Route;
use crate::timeline; use crate::timeline;
use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter}; use crate::timeline::{MergeKind, NoteRef, Timeline, ViewFilter};
use crate::ui; use crate::ui;
use crate::ui::profile::SimpleProfilePreviewController; use crate::ui::{DesktopSidePanel, RelayView, SidePanelAction, View};
use crate::ui::DesktopSidePanel;
use crate::Result; 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::{Context, Frame, Style};
use egui_extras::{Size, StripBuilder}; use egui_extras::{Size, StripBuilder};
@@ -25,8 +29,6 @@ use std::path::Path;
use std::time::Duration; use std::time::Duration;
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use enostr::RelayPool;
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub enum DamusState { pub enum DamusState {
Initializing, Initializing,
@@ -42,7 +44,7 @@ pub struct Damus {
is_mobile: bool, is_mobile: bool,
/// global navigation for account management popups, etc. /// global navigation for account management popups, etc.
_nav: Vec<Route>, //nav: Vec<Route>,
pub textmode: bool, pub textmode: bool,
pub timelines: Vec<Timeline>, 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); 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); 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 timeline = &mut app.timelines[timeline_ind];
let num_prev_items = timeline.notes(filter).len(); let num_prev_items = timeline.notes(filter).len();
let (notes, merge_kind) = timeline::merge_sorted_vecs(timeline.notes(filter), new_refs); 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; timeline.view_mut(filter).notes = notes;
let new_items = timeline.notes(filter).len() - num_prev_items; let new_items = timeline.notes(filter).len() - num_prev_items;
@@ -697,7 +705,6 @@ impl Damus {
img_cache: ImageCache::new(imgcache_dir), img_cache: ImageCache::new(imgcache_dir),
note_cache: NoteCache::default(), note_cache: NoteCache::default(),
selected_timeline: 0, selected_timeline: 0,
_nav: Vec::with_capacity(6),
timelines, timelines,
textmode: false, textmode: false,
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"), 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(), note_cache: NoteCache::default(),
selected_timeline: 0, selected_timeline: 0,
timelines, timelines,
_nav: vec![],
textmode: false, textmode: false,
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"), 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), 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) { fn render_damus_mobile(ctx: &egui::Context, app: &mut Damus) {
//render_panel(ctx, app, 0); //render_panel(ctx, app, 0);
#[cfg(feature = "profiling")] #[cfg(feature = "profiling")]
puffin::profile_function!(); puffin::profile_function!();
//let routes = app.timelines[0].routes.clone();
main_panel(&ctx.style(), app.is_mobile()).show(ctx, |ui| { 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) .clip(true)
.horizontal(|mut strip| { .horizontal(|mut strip| {
strip.cell(|ui| { strip.cell(|ui| {
let side_panel = DesktopSidePanel::new( let side_panel = DesktopSidePanel::new(app).show(ui);
app.account_manager
.get_selected_account()
.map(|a| a.pubkey.bytes()),
SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
)
.show(ui);
if side_panel.response.clicked() { if side_panel.response.clicked() {
info!("clicked {:?}", side_panel.action); 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 { 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));
} }
}); });
} }

View File

@@ -1,9 +1,24 @@
//use nostrdb::NoteKey; use enostr::NoteId;
use std::fmt;
/// App routing. These describe different places you can go inside Notedeck. /// App routing. These describe different places you can go inside Notedeck.
#[derive(Clone, Debug)]
pub enum Route { pub enum Route {
/* Timeline(String),
ManageAccount, 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"),
}
}
} }

View File

@@ -1,13 +1,9 @@
use std::path::Path; use std::path::Path;
use enostr::{FullKeypair, Pubkey, RelayPool}; use enostr::{FullKeypair, Pubkey, RelayPool};
use nostrdb::{Config, Ndb, ProfileRecord}; use nostrdb::ProfileRecord;
use crate::{ use crate::{account_manager::UserAccount, Damus};
account_manager::{AccountManager, UserAccount},
imgcache::ImageCache,
key_storage::KeyStorage,
};
#[allow(unused_must_use)] #[allow(unused_must_use)]
pub fn sample_pool() -> RelayPool { pub fn sample_pool() -> RelayPool {
@@ -87,20 +83,15 @@ pub fn get_test_accounts() -> Vec<UserAccount> {
.collect() .collect()
} }
pub fn get_accmgr_and_ndb_and_imgcache() -> (AccountManager, Ndb, ImageCache) { pub fn get_account_manager_test_app(is_mobile: bool) -> Damus {
let mut account_manager = AccountManager::new(None, KeyStorage::None);
let accounts = get_test_accounts();
for account in accounts {
account_manager.add_account(account);
}
let mut config = Config::new();
config.set_ingester_threads(2);
let db_dir = Path::new("."); let db_dir = Path::new(".");
let path = db_dir.to_str().unwrap(); let path = db_dir.to_str().unwrap();
let ndb = Ndb::new(path, &config).expect("ndb"); let mut app = Damus::mock(path, is_mobile);
let imgcache_dir = db_dir.join("cache/img");
let img_cache = ImageCache::new(imgcache_dir); let accounts = get_test_accounts();
(account_manager, ndb, img_cache) for account in accounts {
app.account_manager.add_account(account);
}
app
} }

View File

@@ -1,11 +1,14 @@
use crate::notecache::CachedNote; use crate::notecache::CachedNote;
use crate::{ui, Damus}; use crate::{ui, Damus};
use crate::route::Route;
use egui::containers::scroll_area::ScrollBarVisibility; use egui::containers::scroll_area::ScrollBarVisibility;
use egui::{Direction, Layout}; use egui::{Direction, Layout};
use crate::ui::BarAction;
use egui_tabs::TabColor; use egui_tabs::TabColor;
use egui_virtual_list::VirtualList; use egui_virtual_list::VirtualList;
use enostr::Filter; use enostr::{Filter, NoteId};
use nostrdb::{Note, NoteKey, Subscription, Transaction}; use nostrdb::{Note, NoteKey, Subscription, Transaction};
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::Ordering; 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 { pub enum ViewFilter {
Notes, Notes,
NotesAndReplies, NotesAndReplies,
@@ -122,6 +125,7 @@ pub struct Timeline {
pub filter: Vec<Filter>, pub filter: Vec<Filter>,
pub views: Vec<TimelineView>, pub views: Vec<TimelineView>,
pub selected_view: i32, pub selected_view: i32,
pub routes: Vec<Route>,
/// Our nostrdb subscription /// Our nostrdb subscription
pub subscription: Option<Subscription>, pub subscription: Option<Subscription>,
@@ -134,12 +138,14 @@ impl Timeline {
let replies = TimelineView::new(ViewFilter::NotesAndReplies); let replies = TimelineView::new(ViewFilter::NotesAndReplies);
let views = vec![notes, replies]; let views = vec![notes, replies];
let selected_view = 0; let selected_view = 0;
let routes = vec![Route::Timeline("Timeline".to_string())];
Timeline { Timeline {
filter, filter,
views, views,
subscription, subscription,
selected_view, selected_view,
routes,
} }
} }
@@ -233,7 +239,7 @@ fn tabs_ui(timeline: &mut Timeline, ui: &mut egui::Ui) {
let stroke = egui::Stroke { let stroke = egui::Stroke {
color: ui.visuals().hyperlink_color, color: ui.visuals().hyperlink_color,
width: 3.0, width: 2.0,
}; };
let speed = 0.1f32; 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| { ui::padding(8.0, ui, |ui| {
let textmode = app.textmode; let textmode = app.textmode;
ui.add(ui::Note::new(app, &note).note_previews(!textmode)); let resp = ui::Note::new(app, &note).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::hline(ui);
//ui.add(egui::Separator::default().spacing(0.0)); //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 { pub enum MergeKind {
FrontInsert, FrontInsert,
Spliced, Spliced,

View File

@@ -2,103 +2,69 @@ use crate::colors::PINK;
use crate::{ use crate::{
account_manager::AccountManager, account_manager::AccountManager,
app_style::NotedeckTextStyle, 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 egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2};
use super::profile::preview::SimpleProfilePreview; use super::profile::preview::SimpleProfilePreview;
use super::profile::{ProfilePreviewOp, SimpleProfilePreviewController}; use super::profile::ProfilePreviewOp;
pub struct AccountManagementView<'a> { pub struct AccountManagementView {}
mobile: bool,
account_manager: &'a mut AccountManager,
simple_preview_controller: SimpleProfilePreviewController<'a>,
}
impl<'a> View for AccountManagementView<'a> { impl AccountManagementView {
fn ui(&mut self, ui: &mut egui::Ui) { fn show(app: &mut Damus, 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) {
Frame::none().outer_margin(24.0).show(ui, |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); ui.add_space(8.0);
scroll_area().show(ui, |ui| { scroll_area().show(ui, |ui| {
self.show_accounts(ui); Self::show_accounts(app, ui);
}); });
}); });
} }
fn show_accounts(&mut self, ui: &mut egui::Ui) { fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) {
let maybe_remove = self.simple_preview_controller.set_profile_previews( let maybe_remove =
self.account_manager, profile_preview_controller::set_profile_previews(app, ui, account_card_ui());
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( ui.allocate_ui_with_layout(
Vec2::new(ui.available_size_before_wrap().x, 32.0), Vec2::new(ui.available_size_before_wrap().x, 32.0),
Layout::top_down(egui::Align::Min), Layout::top_down(egui::Align::Min),
|ui| { |ui| {
// create all account 'cards' and get the indicies the user requested to remove // create all account 'cards' and get the indicies the user requested to remove
let maybe_remove = self.simple_preview_controller.set_profile_previews( let maybe_remove = profile_preview_controller::set_profile_previews(
self.account_manager, app,
ui, ui,
account_card_ui(), // closure for creating an account 'card' account_card_ui(), // closure for creating an account 'card'
); );
// remove all account indicies user requested // 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 { if let Some(to_remove) = account_indices {
to_remove to_remove
.iter() .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 { fn show_mobile(app: &mut Damus, ui: &mut egui::Ui) {
egui::CentralPanel::default()
.show(ui.ctx(), |ui| {
mobile_title(ui); mobile_title(ui);
self.top_section_buttons_widget(ui); Self::top_section_buttons_widget(ui);
ui.add_space(8.0); ui.add_space(8.0);
scroll_area().show(ui, |ui| { scroll_area().show(ui, |ui| Self::show_accounts_mobile(app, ui));
self.show_accounts_mobile(ui);
});
})
.response
} }
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.horizontal(|ui| {
ui.allocate_ui_with_layout( ui.allocate_ui_with_layout(
Vec2::new(ui.available_size_before_wrap().x, 32.0), Vec2::new(ui.available_size_before_wrap().x, 32.0),
@@ -233,44 +199,36 @@ fn selected_widget() -> impl egui::Widget {
// PREVIEWS // PREVIEWS
mod preview { mod preview {
use nostrdb::Ndb;
use super::*; 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 { pub struct AccountManagementPreview {
is_mobile: bool, is_mobile: bool,
account_manager: AccountManager, app: Damus,
ndb: Ndb,
img_cache: ImageCache,
} }
impl AccountManagementPreview { impl AccountManagementPreview {
fn new(is_mobile: bool) -> Self { 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 { AccountManagementPreview { is_mobile, app }
is_mobile,
account_manager,
ndb,
img_cache,
}
} }
} }
impl View for AccountManagementPreview { impl View for AccountManagementPreview {
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
ui.add_space(24.0); ui.add_space(24.0);
AccountManagementView::new( if self.is_mobile {
self.is_mobile, AccountManagementView::show_mobile(&mut self.app, ui);
&mut self.account_manager, } else {
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache), AccountManagementView::show(&mut self.app, ui);
) }
.ui(ui);
} }
} }
impl<'a> Preview for AccountManagementView<'a> { impl Preview for AccountManagementView {
type Prev = AccountManagementPreview; type Prev = AccountManagementPreview;
fn preview(cfg: PreviewConfig) -> Self::Prev { fn preview(cfg: PreviewConfig) -> Self::Prev {

View File

@@ -1,21 +1,18 @@
use crate::{ use crate::{
account_manager::{AccountManager, UserAccount}, account_manager::UserAccount, colors::PINK, profile::DisplayName,
colors::PINK, ui::profile_preview_controller, Damus, Result,
profile::DisplayName,
Result,
}; };
use nostrdb::Ndb;
use egui::{ use egui::{
Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea, Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea,
Sense, Vec2, Sense, Vec2,
}; };
use super::profile::{preview::SimpleProfilePreview, SimpleProfilePreviewController}; use super::profile::preview::SimpleProfilePreview;
pub struct AccountSelectionWidget<'a> { pub struct AccountSelectionWidget {}
is_mobile: bool,
account_manager: &'a AccountManager,
simple_preview_controller: SimpleProfilePreviewController<'a>,
}
enum AccountSelectAction { enum AccountSelectAction {
RemoveAccount { _index: usize }, RemoveAccount { _index: usize },
@@ -28,36 +25,24 @@ struct AccountSelectResponse {
action: Option<AccountSelectAction>, action: Option<AccountSelectAction>,
} }
impl<'a> AccountSelectionWidget<'a> { impl AccountSelectionWidget {
pub fn new( pub fn ui(app: &mut Damus, ui: &mut egui::Ui) {
is_mobile: bool, if app.is_mobile() {
account_manager: &'a AccountManager, Self::show_mobile(ui);
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);
} else { } 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 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| { Frame::none().outer_margin(8.0).show(ui, |ui| {
res = top_section_widget(ui); res = top_section_widget(ui);
scroll_area().show(ui, |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); selected_index = Some(_index);
res.action = Some(AccountSelectAction::SelectAccount { _index }); res.action = Some(AccountSelectAction::SelectAccount { _index });
} }
@@ -66,9 +51,9 @@ impl<'a> AccountSelectionWidget<'a> {
ui.add(add_account_button()); ui.add(add_account_button());
if let Some(_index) = selected_index { 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); 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 }) res.action = Some(AccountSelectAction::RemoveAccount { _index })
} }
} }
@@ -80,29 +65,30 @@ impl<'a> AccountSelectionWidget<'a> {
res res
} }
fn handle_sign_out(&mut self, ui: &mut egui::Ui, account: &UserAccount) -> bool { fn handle_sign_out(ndb: &Ndb, ui: &mut egui::Ui, account: &UserAccount) -> bool {
if let Ok(response) = self.sign_out_button(ui, account) { if let Ok(response) = Self::sign_out_button(ndb, ui, account) {
response.clicked() response.clicked()
} else { } else {
false false
} }
} }
fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response { fn show_mobile(ui: &mut egui::Ui) -> egui::Response {
let _ = ui; let _ = ui;
todo!() todo!()
} }
fn show_accounts(&mut self, ui: &mut egui::Ui) -> Option<usize> { fn show_accounts(app: &mut Damus, ui: &mut egui::Ui) -> Option<usize> {
self.simple_preview_controller.view_profile_previews( profile_preview_controller::view_profile_previews(app, ui, account_switcher_card_ui)
self.account_manager,
ui,
account_switcher_card_ui(),
)
} }
fn sign_out_button(&self, ui: &mut egui::Ui, account: &UserAccount) -> Result<egui::Response> { fn sign_out_button(
self.simple_preview_controller.show_with_nickname( ndb: &Ndb,
ui: &mut egui::Ui,
account: &UserAccount,
) -> Result<egui::Response> {
profile_preview_controller::show_with_nickname(
ndb,
ui, ui,
account.pubkey.bytes(), account.pubkey.bytes(),
|ui: &mut egui::Ui, username: &DisplayName| { |ui: &mut egui::Ui, username: &DisplayName| {
@@ -122,14 +108,13 @@ impl<'a> AccountSelectionWidget<'a> {
} }
} }
fn account_switcher_card_ui() -> fn( fn account_switcher_card_ui(
ui: &mut egui::Ui, ui: &mut egui::Ui,
preview: SimpleProfilePreview, preview: SimpleProfilePreview,
width: f32, width: f32,
is_selected: bool, is_selected: bool,
index: usize, index: usize,
) -> bool { ) -> bool {
|ui, preview, width, is_selected, index| {
let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| { let resp = ui.add_sized(Vec2::new(width, 50.0), |ui: &mut egui::Ui| {
Frame::none() Frame::none()
.show(ui, |ui| { .show(ui, |ui| {
@@ -157,7 +142,6 @@ fn account_switcher_card_ui() -> fn(
ui.interact(resp.rect, Id::new(index), Sense::click()) ui.interact(resp.rect, Id::new(index), Sense::click())
.clicked() .clicked()
}
} }
fn selection_widget() -> impl egui::Widget { fn selection_widget() -> impl egui::Widget {
@@ -215,48 +199,33 @@ fn add_account_button() -> egui::Button<'static> {
} }
mod previews { mod previews {
use nostrdb::Ndb;
use crate::{ use crate::{
account_manager::AccountManager,
imgcache::ImageCache,
test_data, test_data,
ui::{profile::SimpleProfilePreviewController, Preview, PreviewConfig, View}, ui::{Preview, PreviewConfig, View},
Damus,
}; };
use super::AccountSelectionWidget; use super::AccountSelectionWidget;
pub struct AccountSelectionPreview { pub struct AccountSelectionPreview {
is_mobile: bool, app: Damus,
account_manager: AccountManager,
ndb: Ndb,
img_cache: ImageCache,
} }
impl AccountSelectionPreview { impl AccountSelectionPreview {
fn new(is_mobile: bool) -> Self { fn new(is_mobile: bool) -> Self {
let (account_manager, ndb, img_cache) = test_data::get_accmgr_and_ndb_and_imgcache(); let app = test_data::get_account_manager_test_app(is_mobile);
AccountSelectionPreview { AccountSelectionPreview { app }
is_mobile,
account_manager,
ndb,
img_cache,
}
} }
} }
impl View for AccountSelectionPreview { impl View for AccountSelectionPreview {
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
AccountSelectionWidget::new( AccountSelectionWidget::show(&mut self.app, ui);
self.is_mobile,
&self.account_manager,
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
)
.ui(ui);
} }
} }
impl<'a> Preview for AccountSelectionWidget<'a> { impl Preview for AccountSelectionWidget {
type Prev = AccountSelectionPreview; type Prev = AccountSelectionPreview;
fn preview(cfg: PreviewConfig) -> Self::Prev { fn preview(cfg: PreviewConfig) -> Self::Prev {

View File

@@ -13,11 +13,11 @@ pub mod username;
pub use account_management::AccountManagementView; pub use account_management::AccountManagementView;
pub use account_switcher::AccountSelectionWidget; pub use account_switcher::AccountSelectionWidget;
pub use mention::Mention; pub use mention::Mention;
pub use note::Note; pub use note::{BarAction, Note, NoteResponse};
pub use preview::{Preview, PreviewApp, PreviewConfig}; 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 relay::RelayView;
pub use side_panel::DesktopSidePanel; pub use side_panel::{DesktopSidePanel, SidePanelAction};
pub use username::Username; pub use username::Username;
use egui::Margin; use egui::Margin;

View File

@@ -15,12 +15,17 @@ pub struct Note<'a> {
flags: NoteOptions, flags: NoteOptions,
} }
pub struct NoteResponse {
pub response: egui::Response,
pub action: Option<BarAction>,
}
impl<'a> egui::Widget for Note<'a> { impl<'a> egui::Widget for Note<'a> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response { fn ui(self, ui: &mut egui::Ui) -> egui::Response {
if self.app.textmode { if self.app.textmode {
self.textmode_ui(ui) self.textmode_ui(ui)
} else { } else {
self.standard_ui(ui) self.show(ui).response
} }
} }
} }
@@ -186,13 +191,15 @@ impl<'a> Note<'a> {
.response .response
} }
pub fn standard_ui(self, ui: &mut egui::Ui) -> egui::Response { pub fn show(self, ui: &mut egui::Ui) -> NoteResponse {
#[cfg(feature = "profiling")] #[cfg(feature = "profiling")]
puffin::profile_function!(); puffin::profile_function!();
let note_key = self.note.key().expect("todo: support non-db notes"); let note_key = self.note.key().expect("todo: support non-db notes");
let txn = self.note.txn().expect("todo: support non-db notes"); let txn = self.note.txn().expect("todo: support non-db notes");
let mut note_action: Option<BarAction> = None;
ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { let response = ui
.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| {
ui.spacing_mut().item_spacing.x = 16.0; ui.spacing_mut().item_spacing.x = 16.0;
let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey()); let profile = self.app.ndb.get_profile_by_pubkey(txn, self.note.pubkey());
@@ -274,15 +281,25 @@ impl<'a> Note<'a> {
)); ));
if self.options().has_actionbar() { if self.options().has_actionbar() {
render_note_actionbar(ui); note_action = render_note_actionbar(ui).inner;
} }
}); });
}) })
.response .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| { ui.horizontal(|ui| {
let img_data = if ui.style().visuals.dark_mode { let img_data = if ui.style().visuals.dark_mode {
egui::include_image!("../../../assets/icons/reply.png") 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), .fill(ui.style().visuals.panel_fill),
) )
.clicked() .clicked()
{} {
Some(BarAction::Reply)
//if ui.add(egui::Button::new("like")).clicked() {} } else {
None
}
}) })
} }

View File

@@ -1,7 +1,7 @@
pub mod picture; pub mod picture;
pub mod preview; pub mod preview;
mod profile_preview_controller; pub mod profile_preview_controller;
pub use picture::ProfilePic; pub use picture::ProfilePic;
pub use preview::ProfilePreview; pub use preview::ProfilePreview;
pub use profile_preview_controller::{ProfilePreviewOp, SimpleProfilePreviewController}; pub use profile_preview_controller::ProfilePreviewOp;

View File

@@ -1,31 +1,20 @@
use nostrdb::{Ndb, Transaction}; use nostrdb::{Ndb, Transaction};
use crate::{account_manager::AccountManager, imgcache::ImageCache, DisplayName, Result}; use crate::{Damus, DisplayName, Result};
use super::{ use super::{
preview::{get_display_name, get_profile_url, SimpleProfilePreview}, preview::{get_display_name, get_profile_url, SimpleProfilePreview},
ProfilePic, ProfilePic,
}; };
pub struct SimpleProfilePreviewController<'a> {
ndb: &'a Ndb,
img_cache: &'a mut ImageCache,
}
#[derive(Debug)] #[derive(Debug)]
pub enum ProfilePreviewOp { pub enum ProfilePreviewOp {
RemoveAccount, RemoveAccount,
SwitchTo, SwitchTo,
} }
impl<'a> SimpleProfilePreviewController<'a> { pub fn set_profile_previews(
pub fn new(ndb: &'a Ndb, img_cache: &'a mut ImageCache) -> Self { app: &mut Damus,
SimpleProfilePreviewController { ndb, img_cache }
}
pub fn set_profile_previews(
&mut self,
account_manager: &mut AccountManager,
ui: &mut egui::Ui, ui: &mut egui::Ui,
add_preview_ui: fn( add_preview_ui: fn(
ui: &mut egui::Ui, ui: &mut egui::Ui,
@@ -33,34 +22,34 @@ impl<'a> SimpleProfilePreviewController<'a> {
width: f32, width: f32,
is_selected: bool, is_selected: bool,
) -> Option<ProfilePreviewOp>, ) -> Option<ProfilePreviewOp>,
) -> Option<Vec<usize>> { ) -> Option<Vec<usize>> {
let mut to_remove: Option<Vec<usize>> = None; let mut to_remove: Option<Vec<usize>> = None;
let width = ui.available_width(); let width = ui.available_width();
let txn = if let Ok(txn) = Transaction::new(self.ndb) { let txn = if let Ok(txn) = Transaction::new(&app.ndb) {
txn txn
} else { } else {
return None; return None;
}; };
for i in 0..account_manager.num_accounts() { for i in 0..app.account_manager.num_accounts() {
let account = if let Some(account) = account_manager.get_account(i) { let account = if let Some(account) = app.account_manager.get_account(i) {
account account
} else { } else {
continue; continue;
}; };
let profile = let profile =
if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) { if let Ok(profile) = app.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
profile profile
} else { } else {
continue; 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() { let is_selected = if let Some(selected) = app.account_manager.get_selected_account_index() {
i == selected i == selected
} else { } else {
false false
@@ -79,16 +68,15 @@ impl<'a> SimpleProfilePreviewController<'a> {
} }
to_remove.as_mut().unwrap().push(i); to_remove.as_mut().unwrap().push(i);
} }
ProfilePreviewOp::SwitchTo => account_manager.select_account(i), ProfilePreviewOp::SwitchTo => app.account_manager.select_account(i),
} }
} }
to_remove to_remove
} }
pub fn view_profile_previews( pub fn view_profile_previews(
&mut self, app: &mut Damus,
account_manager: &AccountManager,
ui: &mut egui::Ui, ui: &mut egui::Ui,
add_preview_ui: fn( add_preview_ui: fn(
ui: &mut egui::Ui, ui: &mut egui::Ui,
@@ -97,32 +85,32 @@ impl<'a> SimpleProfilePreviewController<'a> {
is_selected: bool, is_selected: bool,
index: usize, index: usize,
) -> bool, ) -> bool,
) -> Option<usize> { ) -> Option<usize> {
let width = ui.available_width(); let width = ui.available_width();
let txn = if let Ok(txn) = Transaction::new(self.ndb) { let txn = if let Ok(txn) = Transaction::new(&app.ndb) {
txn txn
} else { } else {
return None; return None;
}; };
for i in 0..account_manager.num_accounts() { for i in 0..app.account_manager.num_accounts() {
let account = if let Some(account) = account_manager.get_account(i) { let account = if let Some(account) = app.account_manager.get_account(i) {
account account
} else { } else {
continue; continue;
}; };
let profile = let profile =
if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) { if let Ok(profile) = app.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
profile profile
} else { } else {
continue; 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() { let is_selected = if let Some(selected) = app.account_manager.get_selected_account_index() {
i == selected i == selected
} else { } else {
false false
@@ -134,35 +122,34 @@ impl<'a> SimpleProfilePreviewController<'a> {
} }
None None
} }
pub fn show_with_nickname( pub fn show_with_nickname(
&self, ndb: &Ndb,
ui: &mut egui::Ui, ui: &mut egui::Ui,
key: &[u8; 32], key: &[u8; 32],
ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response, ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response,
) -> Result<egui::Response> { ) -> Result<egui::Response> {
let txn = Transaction::new(self.ndb)?; let txn = Transaction::new(ndb)?;
let profile = self.ndb.get_profile_by_pubkey(&txn, key)?; let profile = ndb.get_profile_by_pubkey(&txn, key)?;
Ok(ui_element(ui, &get_display_name(&profile))) Ok(ui_element(ui, &get_display_name(&profile)))
} }
pub fn show_with_pfp( pub fn show_with_pfp(
self, app: &mut Damus,
ui: &mut egui::Ui, ui: &mut egui::Ui,
key: &[u8; 32], key: &[u8; 32],
ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response, ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response,
) -> Option<egui::Response> { ) -> Option<egui::Response> {
if let Ok(txn) = Transaction::new(self.ndb) { if let Ok(txn) = Transaction::new(&app.ndb) {
let profile = self.ndb.get_profile_by_pubkey(&txn, key); let profile = app.ndb.get_profile_by_pubkey(&txn, key);
if let Ok(profile) = profile { if let Ok(profile) = profile {
return Some(ui_element( return Some(ui_element(
ui, ui,
ProfilePic::new(self.img_cache, get_profile_url(&profile)), ProfilePic::new(&mut app.img_cache, get_profile_url(&profile)),
)); ));
} }
} }
None None
}
} }

View File

@@ -1,12 +1,17 @@
use egui::{Button, Layout, SidePanel, Vec2, Widget}; 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> { pub struct DesktopSidePanel<'a> {
selected_account: Option<&'a [u8; 32]>, app: &'a mut Damus,
simple_preview_controller: SimpleProfilePreviewController<'a>, }
impl<'a> View for DesktopSidePanel<'a> {
fn ui(&mut self, ui: &mut egui::Ui) {
self.show(ui);
}
} }
#[derive(Debug, Eq, PartialEq, Clone, Copy)] #[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> { impl<'a> DesktopSidePanel<'a> {
pub fn new( pub fn new(app: &'a mut Damus) -> Self {
selected_account: Option<&'a [u8; 32]>, DesktopSidePanel { app }
simple_preview_controller: SimpleProfilePreviewController<'a>,
) -> Self {
DesktopSidePanel {
selected_account,
simple_preview_controller,
}
} }
pub fn panel() -> SidePanel { pub fn panel() -> SidePanel {
@@ -51,7 +44,7 @@ impl<'a> DesktopSidePanel<'a> {
.exact_width(40.0) .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 dark_mode = ui.ctx().style().visuals.dark_mode;
let spacing_amt = 16.0; let spacing_amt = 16.0;
@@ -77,12 +70,15 @@ impl<'a> DesktopSidePanel<'a> {
SidePanelResponse::new(inner.inner, inner.response) SidePanelResponse::new(inner.inner, inner.response)
} }
fn pfp_button(self, ui: &mut egui::Ui) -> egui::Response { fn pfp_button(&mut self, ui: &mut egui::Ui) -> egui::Response {
if let Some(selected_account) = self.selected_account { let selected_account = self.app.account_manager.get_selected_account();
if let Some(response) = if let Some(selected_account) = selected_account {
self.simple_preview_controller if let Some(response) = profile_preview_controller::show_with_pfp(
.show_with_pfp(ui, selected_account, show_pfp()) self.app,
{ ui,
&selected_account.pubkey.bytes().clone(),
show_pfp(),
) {
return response; return response;
} }
} }
@@ -123,10 +119,9 @@ fn add_column_button(dark_mode: bool) -> egui::Button<'static> {
} }
mod preview { mod preview {
use nostrdb::Ndb;
use crate::{ use crate::{
imgcache::ImageCache,
test_data, test_data,
ui::{Preview, PreviewConfig}, ui::{Preview, PreviewConfig},
}; };
@@ -134,33 +129,25 @@ mod preview {
use super::*; use super::*;
pub struct DesktopSidePanelPreview { pub struct DesktopSidePanelPreview {
account_manager: AccountManager, app: Damus,
ndb: Ndb,
img_cache: ImageCache,
} }
impl DesktopSidePanelPreview { impl DesktopSidePanelPreview {
fn new() -> Self { fn new(is_mobile: bool) -> Self {
let (account_manager, ndb, img_cache) = test_data::get_accmgr_and_ndb_and_imgcache(); let app = test_data::get_account_manager_test_app(is_mobile);
DesktopSidePanelPreview { DesktopSidePanelPreview { app }
account_manager,
ndb,
img_cache,
}
} }
} }
impl View for DesktopSidePanelPreview { impl View for DesktopSidePanelPreview {
fn ui(&mut self, ui: &mut egui::Ui) { fn ui(&mut self, ui: &mut egui::Ui) {
let selected_account = self let _selected_account = self
.app
.account_manager .account_manager
.get_selected_account() .get_selected_account()
.map(|x| x.pubkey.bytes()); .map(|x| x.pubkey.bytes());
let panel = DesktopSidePanel::new( let mut panel = DesktopSidePanel::new(&mut self.app);
selected_account,
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
);
DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui)); DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui));
} }
@@ -169,8 +156,8 @@ mod preview {
impl<'a> Preview for DesktopSidePanel<'a> { impl<'a> Preview for DesktopSidePanel<'a> {
type Prev = DesktopSidePanelPreview; type Prev = DesktopSidePanelPreview;
fn preview(_cfg: PreviewConfig) -> Self::Prev { fn preview(cfg: PreviewConfig) -> Self::Prev {
DesktopSidePanelPreview::new() DesktopSidePanelPreview::new(cfg.is_mobile)
} }
} }
} }