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

View File

@@ -1,6 +1,5 @@
use crate::route::{Route, Router};
use crate::timeline::{Timeline, TimelineId};
use enostr::Pubkey;
use indexmap::IndexMap;
use std::iter::Iterator;
use std::sync::atomic::{AtomicU32, Ordering};
@@ -61,14 +60,6 @@ impl Columns {
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) {
self.add_column(Column::new(vec![Route::AddColumn]));
}

View File

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

View File

@@ -9,11 +9,18 @@ use crate::{
timeline::TimelineTab, Error, Result,
};
#[derive(Default)]
pub struct NotesHolderStorage<M: NotesHolder> {
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> {
Fresh(&'a mut M),
Stale(&'a mut M),
@@ -75,8 +82,10 @@ impl<M: NotesHolder> NotesHolderStorage<M> {
debug!("found thread with {} notes", notes.len());
}
self.id_to_object
.insert(id.to_owned(), M::new_notes_holder(notes));
self.id_to_object.insert(
id.to_owned(),
M::new_notes_holder(id, M::filters(id), notes),
);
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 filters(for_id: &[u8; 32]) -> 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"]
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 crate::{
filter,
filter::{self, FilterState},
multi_subscriber::MultiSubscriber,
note::NoteRef,
notes_holder::NotesHolder,
timeline::{Timeline, TimelineTab, ViewFilter},
timeline::{PubkeySource, Timeline, TimelineKind},
};
pub enum DisplayName<'a> {
@@ -48,21 +48,18 @@ pub fn get_profile_name<'a>(record: &'a ProfileRecord) -> Option<DisplayName<'a>
}
pub struct Profile {
view: TimelineTab,
pub timeline: Timeline,
pub multi_subscriber: Option<MultiSubscriber>,
}
impl Profile {
pub fn new(notes: Vec<NoteRef>) -> Self {
let mut cap = ((notes.len() as f32) * 1.5) as usize;
if cap == 0 {
cap = 25;
}
let mut view = TimelineTab::new_with_capacity(ViewFilter::NotesAndReplies, cap);
view.notes = notes;
pub fn new(source: PubkeySource, filters: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
let mut timeline =
Timeline::new(TimelineKind::profile(source), FilterState::ready(filters));
timeline.current_view_mut().notes = notes;
Profile {
view,
timeline,
multi_subscriber: None,
}
}
@@ -81,7 +78,7 @@ impl NotesHolder for Profile {
}
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> {
@@ -98,7 +95,11 @@ impl NotesHolder for Profile {
.collect()
}
fn new_notes_holder(notes: Vec<NoteRef>) -> Self {
Profile::new(notes)
fn new_notes_holder(id: &[u8; 32], filters: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
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,
column::Columns,
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.
@@ -17,7 +17,7 @@ pub enum Route {
Relays,
ComposeNote,
AddColumn,
Profile(Pubkey, TimelineId),
Profile(Pubkey),
}
#[derive(Clone)]
@@ -97,11 +97,8 @@ impl Route {
},
Route::ComposeNote => "Compose Note".to_owned(),
Route::AddColumn => "Add Column".to_owned(),
Route::Profile(_, id) => {
let timeline = columns
.find_timeline(*id)
.expect("expected to find timeline");
timeline.kind.to_title(ndb)
Route::Profile(pubkey) => {
format!("{}'s Profile", get_profile_displayname_string(ndb, pubkey))
}
};
@@ -210,7 +207,7 @@ impl fmt::Display for Route {
Route::ComposeNote => write!(f, "Compose Note"),
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)
}
fn new_notes_holder(notes: Vec<NoteRef>) -> Self {
fn new_notes_holder(_: &[u8; 32], _: Vec<Filter>, notes: Vec<NoteRef>) -> Self {
Thread::new(notes)
}

View File

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

View File

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

View File

@@ -167,7 +167,7 @@ fn timeline_ui_no_scroll(
.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;
let tab_res = egui_tabs::Tabs::new(2)