integrate profile view caching

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2024-10-14 12:53:11 -04:00
parent 705a4bdf05
commit 6ffe33e924
10 changed files with 94 additions and 80 deletions

View File

@@ -14,6 +14,7 @@ use crate::{
note::NoteRef, note::NoteRef,
notecache::{CachedNote, NoteCache}, notecache::{CachedNote, NoteCache},
notes_holder::NotesHolderStorage, notes_holder::NotesHolderStorage,
profile::Profile,
subscriptions::{SubKind, Subscriptions}, subscriptions::{SubKind, Subscriptions},
thread::Thread, thread::Thread,
timeline::{Timeline, TimelineId, TimelineKind, ViewFilter}, timeline::{Timeline, TimelineId, TimelineKind, ViewFilter},
@@ -55,6 +56,7 @@ pub struct Damus {
pub unknown_ids: UnknownIds, pub unknown_ids: UnknownIds,
pub drafts: Drafts, pub drafts: Drafts,
pub threads: NotesHolderStorage<Thread>, pub threads: NotesHolderStorage<Thread>,
pub profiles: NotesHolderStorage<Profile>,
pub img_cache: ImageCache, pub img_cache: ImageCache,
pub accounts: AccountManager, pub accounts: AccountManager,
pub subscriptions: Subscriptions, pub subscriptions: Subscriptions,
@@ -711,6 +713,7 @@ impl Damus {
subscriptions: Subscriptions::default(), subscriptions: Subscriptions::default(),
since_optimize: parsed_args.since_optimize, since_optimize: parsed_args.since_optimize,
threads: NotesHolderStorage::default(), threads: NotesHolderStorage::default(),
profiles: NotesHolderStorage::default(),
drafts: Drafts::default(), drafts: Drafts::default(),
state: DamusState::Initializing, state: DamusState::Initializing,
img_cache: ImageCache::new(imgcache_dir.into()), img_cache: ImageCache::new(imgcache_dir.into()),
@@ -792,6 +795,7 @@ impl Damus {
subscriptions: Subscriptions::default(), subscriptions: Subscriptions::default(),
since_optimize: true, since_optimize: true,
threads: NotesHolderStorage::default(), threads: NotesHolderStorage::default(),
profiles: NotesHolderStorage::default(),
drafts: Drafts::default(), drafts: Drafts::default(),
state: DamusState::Initializing, state: DamusState::Initializing,
pool: RelayPool::new(), pool: RelayPool::new(),

View File

@@ -1,6 +1,5 @@
use crate::route::{Route, Router}; use crate::route::{Route, Router};
use crate::timeline::{Timeline, TimelineId}; use crate::timeline::{Timeline, TimelineId};
use enostr::Pubkey;
use indexmap::IndexMap; use indexmap::IndexMap;
use std::iter::Iterator; use std::iter::Iterator;
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
@@ -61,14 +60,6 @@ impl Columns {
self.timelines.insert(col_id, timeline); self.timelines.insert(col_id, timeline);
} }
pub fn route_profile_timeline(&mut self, col: usize, pubkey: Pubkey, timeline: Timeline) {
self.column_mut(col)
.router_mut()
.route_to(Route::Profile(pubkey, timeline.id));
self.timelines.insert(Self::get_new_id(), timeline);
}
pub fn new_column_picker(&mut self) { pub fn new_column_picker(&mut self) {
self.add_column(Column::new(vec![Route::AddColumn])); self.add_column(Column::new(vec![Route::AddColumn]));
} }

View File

@@ -3,12 +3,13 @@ use crate::{
app_style::{get_font_size, NotedeckTextStyle}, app_style::{get_font_size, NotedeckTextStyle},
fonts::NamedFontFamily, fonts::NamedFontFamily,
notes_holder::NotesHolder, notes_holder::NotesHolder,
profile::Profile,
relay_pool_manager::RelayPoolManager, relay_pool_manager::RelayPoolManager,
route::Route, route::Route,
thread::Thread, thread::Thread,
timeline::{ timeline::{
route::{render_profile_route, render_timeline_route, AfterRouteExecution, TimelineRoute}, route::{render_profile_route, render_timeline_route, AfterRouteExecution, TimelineRoute},
PubkeySource, Timeline, TimelineKind, Timeline,
}, },
ui::{ ui::{
self, self,
@@ -115,11 +116,11 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
None None
} }
Route::Profile(pubkey, id) => render_profile_route( Route::Profile(pubkey) => render_profile_route(
*id,
*pubkey, *pubkey,
&app.ndb, &app.ndb,
&mut app.columns, &mut app.columns,
&mut app.profiles,
&mut app.pool, &mut app.pool,
&mut app.img_cache, &mut app.img_cache,
&mut app.note_cache, &mut app.note_cache,
@@ -144,18 +145,19 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
} }
AfterRouteExecution::OpenProfile(pubkey) => { AfterRouteExecution::OpenProfile(pubkey) => {
let pubkey_source = match app.accounts.get_selected_account() { app.columns
Some(account) if account.pubkey == pubkey => PubkeySource::DeckAuthor, .column_mut(col)
_ => PubkeySource::Explicit(pubkey), .router_mut()
}; .route_to(Route::Profile(pubkey));
let txn = Transaction::new(&app.ndb).expect("txn");
if let Some(timeline) = if let Some(res) = Profile::open(
TimelineKind::profile(pubkey_source).into_timeline(&app.ndb, None) &app.ndb,
{ &txn,
let timeline_id = timeline.id; &mut app.pool,
app.columns_mut() &mut app.profiles,
.route_profile_timeline(col, pubkey, timeline); pubkey.bytes(),
app.subscribe_new_timeline(timeline_id); ) {
res.process(&app.ndb, &txn, &mut app.profiles);
} }
} }
} }
@@ -163,8 +165,8 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
if let Some(NavAction::Returned) = nav_response.action { if let Some(NavAction::Returned) = nav_response.action {
let r = app.columns_mut().column_mut(col).router_mut().pop(); let r = app.columns_mut().column_mut(col).router_mut().pop();
let txn = Transaction::new(&app.ndb).expect("txn");
if let Some(Route::Timeline(TimelineRoute::Thread(id))) = r { if let Some(Route::Timeline(TimelineRoute::Thread(id))) = r {
let txn = Transaction::new(&app.ndb).expect("txn");
let root_id = { let root_id = {
crate::note::root_note_id_from_selected_id( crate::note::root_note_id_from_selected_id(
&app.ndb, &app.ndb,
@@ -176,10 +178,14 @@ pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) {
Thread::unsubscribe_locally(&txn, &app.ndb, &mut app.threads, &mut app.pool, root_id); Thread::unsubscribe_locally(&txn, &app.ndb, &mut app.threads, &mut app.pool, root_id);
} }
if let Some(Route::Profile(_, id)) = r { if let Some(Route::Profile(pubkey)) = r {
if let Some(timeline) = app.columns.find_timeline(id) { Profile::unsubscribe_locally(
unsubscribe_timeline(&app.ndb, timeline); &txn,
} &app.ndb,
&mut app.profiles,
&mut app.pool,
pubkey.bytes(),
);
} }
} else if let Some(NavAction::Navigated) = nav_response.action { } else if let Some(NavAction::Navigated) = nav_response.action {
let cur_router = app.columns_mut().column_mut(col).router_mut(); let cur_router = app.columns_mut().column_mut(col).router_mut();

View File

@@ -9,11 +9,18 @@ use crate::{
timeline::TimelineTab, Error, Result, timeline::TimelineTab, Error, Result,
}; };
#[derive(Default)]
pub struct NotesHolderStorage<M: NotesHolder> { pub struct NotesHolderStorage<M: NotesHolder> {
pub id_to_object: HashMap<[u8; 32], M>, pub id_to_object: HashMap<[u8; 32], M>,
} }
impl<M: NotesHolder> Default for NotesHolderStorage<M> {
fn default() -> Self {
NotesHolderStorage {
id_to_object: HashMap::new(),
}
}
}
pub enum Vitality<'a, M> { pub enum Vitality<'a, M> {
Fresh(&'a mut M), Fresh(&'a mut M),
Stale(&'a mut M), Stale(&'a mut M),
@@ -75,8 +82,10 @@ impl<M: NotesHolder> NotesHolderStorage<M> {
debug!("found thread with {} notes", notes.len()); debug!("found thread with {} notes", notes.len());
} }
self.id_to_object self.id_to_object.insert(
.insert(id.to_owned(), M::new_notes_holder(notes)); id.to_owned(),
M::new_notes_holder(id, M::filters(id), notes),
);
Vitality::Fresh(self.id_to_object.get_mut(id).unwrap()) Vitality::Fresh(self.id_to_object.get_mut(id).unwrap())
} }
} }
@@ -87,7 +96,7 @@ pub trait NotesHolder {
fn get_view(&mut self) -> &mut TimelineTab; fn get_view(&mut self) -> &mut TimelineTab;
fn filters(for_id: &[u8; 32]) -> Vec<Filter>; fn filters(for_id: &[u8; 32]) -> Vec<Filter>;
fn filters_since(for_id: &[u8; 32], since: u64) -> Vec<Filter>; fn filters_since(for_id: &[u8; 32], since: u64) -> Vec<Filter>;
fn new_notes_holder(notes: Vec<NoteRef>) -> Self; fn new_notes_holder(id: &[u8; 32], filters: Vec<Filter>, notes: Vec<NoteRef>) -> Self;
#[must_use = "UnknownIds::update_from_note_refs should be used on this result"] #[must_use = "UnknownIds::update_from_note_refs should be used on this result"]
fn poll_notes_into_view(&mut self, txn: &Transaction, ndb: &Ndb) -> Result<()> { fn poll_notes_into_view(&mut self, txn: &Transaction, ndb: &Ndb) -> Result<()> {

View File

@@ -1,12 +1,12 @@
use enostr::Filter; use enostr::{Filter, Pubkey};
use nostrdb::{FilterBuilder, ProfileRecord}; use nostrdb::{FilterBuilder, ProfileRecord};
use crate::{ use crate::{
filter, filter::{self, FilterState},
multi_subscriber::MultiSubscriber, multi_subscriber::MultiSubscriber,
note::NoteRef, note::NoteRef,
notes_holder::NotesHolder, notes_holder::NotesHolder,
timeline::{Timeline, TimelineTab, ViewFilter}, timeline::{PubkeySource, Timeline, TimelineKind},
}; };
pub enum DisplayName<'a> { pub enum DisplayName<'a> {
@@ -48,21 +48,18 @@ pub fn get_profile_name<'a>(record: &'a ProfileRecord) -> Option<DisplayName<'a>
} }
pub struct Profile { pub struct Profile {
view: TimelineTab, pub timeline: Timeline,
pub multi_subscriber: Option<MultiSubscriber>, pub multi_subscriber: Option<MultiSubscriber>,
} }
impl Profile { impl Profile {
pub fn new(notes: Vec<NoteRef>) -> Self { pub fn new(source: PubkeySource, filters: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
let mut cap = ((notes.len() as f32) * 1.5) as usize; let mut timeline =
if cap == 0 { Timeline::new(TimelineKind::profile(source), FilterState::ready(filters));
cap = 25; timeline.current_view_mut().notes = notes;
}
let mut view = TimelineTab::new_with_capacity(ViewFilter::NotesAndReplies, cap);
view.notes = notes;
Profile { Profile {
view, timeline,
multi_subscriber: None, multi_subscriber: None,
} }
} }
@@ -81,7 +78,7 @@ impl NotesHolder for Profile {
} }
fn get_view(&mut self) -> &mut crate::timeline::TimelineTab { fn get_view(&mut self) -> &mut crate::timeline::TimelineTab {
&mut self.view self.timeline.current_view_mut()
} }
fn filters(for_id: &[u8; 32]) -> Vec<enostr::Filter> { fn filters(for_id: &[u8; 32]) -> Vec<enostr::Filter> {
@@ -98,7 +95,11 @@ impl NotesHolder for Profile {
.collect() .collect()
} }
fn new_notes_holder(notes: Vec<NoteRef>) -> Self { fn new_notes_holder(id: &[u8; 32], filters: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
Profile::new(notes) Profile::new(PubkeySource::Explicit(Pubkey::new(*id)), filters, notes)
}
fn set_multi_subscriber(&mut self, subscriber: MultiSubscriber) {
self.multi_subscriber = Some(subscriber);
} }
} }

View File

@@ -6,7 +6,7 @@ use crate::{
account_manager::AccountsRoute, account_manager::AccountsRoute,
column::Columns, column::Columns,
timeline::{TimelineId, TimelineRoute}, timeline::{TimelineId, TimelineRoute},
ui::profile::preview::get_note_users_displayname_string, ui::profile::preview::{get_note_users_displayname_string, get_profile_displayname_string},
}; };
/// App routing. These describe different places you can go inside Notedeck. /// App routing. These describe different places you can go inside Notedeck.
@@ -17,7 +17,7 @@ pub enum Route {
Relays, Relays,
ComposeNote, ComposeNote,
AddColumn, AddColumn,
Profile(Pubkey, TimelineId), Profile(Pubkey),
} }
#[derive(Clone)] #[derive(Clone)]
@@ -97,11 +97,8 @@ impl Route {
}, },
Route::ComposeNote => "Compose Note".to_owned(), Route::ComposeNote => "Compose Note".to_owned(),
Route::AddColumn => "Add Column".to_owned(), Route::AddColumn => "Add Column".to_owned(),
Route::Profile(_, id) => { Route::Profile(pubkey) => {
let timeline = columns format!("{}'s Profile", get_profile_displayname_string(ndb, pubkey))
.find_timeline(*id)
.expect("expected to find timeline");
timeline.kind.to_title(ndb)
} }
}; };
@@ -210,7 +207,7 @@ impl fmt::Display for Route {
Route::ComposeNote => write!(f, "Compose Note"), Route::ComposeNote => write!(f, "Compose Note"),
Route::AddColumn => write!(f, "Add Column"), Route::AddColumn => write!(f, "Add Column"),
Route::Profile(_, _) => write!(f, "Profile"), Route::Profile(_) => write!(f, "Profile"),
} }
} }
} }

View File

@@ -66,7 +66,7 @@ impl NotesHolder for Thread {
Thread::filters(for_id) Thread::filters(for_id)
} }
fn new_notes_holder(notes: Vec<NoteRef>) -> Self { fn new_notes_holder(_: &[u8; 32], _: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
Thread::new(notes) Thread::new(notes)
} }

View File

@@ -5,6 +5,7 @@ use crate::{
imgcache::ImageCache, imgcache::ImageCache,
notecache::NoteCache, notecache::NoteCache,
notes_holder::NotesHolderStorage, notes_holder::NotesHolderStorage,
profile::Profile,
thread::Thread, thread::Thread,
timeline::TimelineId, timeline::TimelineId,
ui::{ ui::{
@@ -156,10 +157,10 @@ pub fn render_timeline_route(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn render_profile_route( pub fn render_profile_route(
id: TimelineId,
pubkey: Pubkey, pubkey: Pubkey,
ndb: &Ndb, ndb: &Ndb,
columns: &mut Columns, columns: &mut Columns,
profiles: &mut NotesHolderStorage<Profile>,
pool: &mut RelayPool, pool: &mut RelayPool,
img_cache: &mut ImageCache, img_cache: &mut ImageCache,
note_cache: &mut NoteCache, note_cache: &mut NoteCache,
@@ -168,7 +169,7 @@ pub fn render_profile_route(
ui: &mut egui::Ui, ui: &mut egui::Ui,
) -> Option<AfterRouteExecution> { ) -> Option<AfterRouteExecution> {
let timeline_response = let timeline_response =
ProfileView::new(pubkey, id, columns, ndb, note_cache, img_cache).ui(ui); ProfileView::new(pubkey, col, profiles, ndb, note_cache, img_cache).ui(ui);
if let Some(bar_action) = timeline_response.bar_action { if let Some(bar_action) = timeline_response.bar_action {
let txn = nostrdb::Transaction::new(ndb).expect("txn"); let txn = nostrdb::Transaction::new(ndb).expect("txn");
let mut cur_column = columns.columns_mut(); let mut cur_column = columns.columns_mut();

View File

@@ -8,16 +8,16 @@ pub use picture::ProfilePic;
pub use preview::ProfilePreview; pub use preview::ProfilePreview;
use crate::{ use crate::{
actionbar::TimelineResponse, column::Columns, imgcache::ImageCache, notecache::NoteCache, actionbar::TimelineResponse, imgcache::ImageCache, notecache::NoteCache,
timeline::TimelineId, notes_holder::NotesHolderStorage, profile::Profile,
}; };
use super::TimelineView; use super::timeline::{tabs_ui, TimelineTabView};
pub struct ProfileView<'a> { pub struct ProfileView<'a> {
pubkey: Pubkey, pubkey: Pubkey,
timeline_id: TimelineId, col_id: usize,
columns: &'a mut Columns, profiles: &'a mut NotesHolderStorage<Profile>,
ndb: &'a Ndb, ndb: &'a Ndb,
note_cache: &'a mut NoteCache, note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache, img_cache: &'a mut ImageCache,
@@ -26,16 +26,16 @@ pub struct ProfileView<'a> {
impl<'a> ProfileView<'a> { impl<'a> ProfileView<'a> {
pub fn new( pub fn new(
pubkey: Pubkey, pubkey: Pubkey,
timeline_id: TimelineId, col_id: usize,
columns: &'a mut Columns, profiles: &'a mut NotesHolderStorage<Profile>,
ndb: &'a Ndb, ndb: &'a Ndb,
note_cache: &'a mut NoteCache, note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache, img_cache: &'a mut ImageCache,
) -> Self { ) -> Self {
ProfileView { ProfileView {
pubkey, pubkey,
timeline_id, col_id,
columns, profiles,
ndb, ndb,
note_cache, note_cache,
img_cache, img_cache,
@@ -43,27 +43,32 @@ impl<'a> ProfileView<'a> {
} }
pub fn ui(&mut self, ui: &mut egui::Ui) -> TimelineResponse { pub fn ui(&mut self, ui: &mut egui::Ui) -> TimelineResponse {
let scroll_id = egui::Id::new(("profile_scroll", self.timeline_id, self.pubkey)); let scroll_id = egui::Id::new(("profile_scroll", self.col_id, self.pubkey));
ScrollArea::vertical() ScrollArea::vertical()
.id_source(scroll_id) .id_source(scroll_id)
.show(ui, |ui| { .show(ui, |ui| {
{ let txn = Transaction::new(self.ndb).expect("txn");
let txn = Transaction::new(self.ndb).expect("txn"); if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, self.pubkey.bytes()) {
if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, self.pubkey.bytes()) { ProfilePreview::new(&profile, self.img_cache).ui(ui);
ProfilePreview::new(&profile, self.img_cache).ui(ui);
}
} }
let profile = self
.profiles
.notes_holder_mutated(self.ndb, &txn, self.pubkey.bytes())
.get_ptr();
TimelineView::new( profile.timeline.selected_view = tabs_ui(ui);
self.timeline_id,
self.columns, TimelineTabView::new(
profile.timeline.current_view(),
false,
false,
&txn,
self.ndb, self.ndb,
self.note_cache, self.note_cache,
self.img_cache, self.img_cache,
false,
) )
.ui_no_scroll(ui) .show(ui)
}) })
.inner .inner
} }

View File

@@ -167,7 +167,7 @@ fn timeline_ui_no_scroll(
.show(ui) .show(ui)
} }
fn tabs_ui(ui: &mut egui::Ui) -> i32 { pub fn tabs_ui(ui: &mut egui::Ui) -> i32 {
ui.spacing_mut().item_spacing.y = 0.0; ui.spacing_mut().item_spacing.y = 0.0;
let tab_res = egui_tabs::Tabs::new(2) let tab_res = egui_tabs::Tabs::new(2)