Merge tombstone muted notes #606

Changelog-Changed: Tombstone muted notes
This commit is contained in:
William Casarin
2025-01-04 14:08:33 -08:00
18 changed files with 134 additions and 125 deletions

View File

@@ -8,7 +8,7 @@ use crate::{
use enostr::{NoteId, Pubkey, RelayPool};
use nostrdb::{Ndb, Transaction};
use notedeck::{note::root_note_id_from_selected_id, MuteFun, NoteCache, NoteRef};
use notedeck::{note::root_note_id_from_selected_id, NoteCache, NoteRef};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum NoteAction {
@@ -41,12 +41,11 @@ fn open_thread(
pool: &mut RelayPool,
threads: &mut NotesHolderStorage<Thread>,
selected_note: &[u8; 32],
is_muted: &MuteFun,
) -> Option<NotesHolderResult> {
router.route_to(Route::thread(NoteId::new(selected_note.to_owned())));
let root_id = root_note_id_from_selected_id(ndb, note_cache, txn, selected_note);
Thread::open(ndb, note_cache, txn, pool, threads, root_id, is_muted)
Thread::open(ndb, note_cache, txn, pool, threads, root_id)
}
impl NoteAction {
@@ -60,7 +59,6 @@ impl NoteAction {
note_cache: &mut NoteCache,
pool: &mut RelayPool,
txn: &Transaction,
is_muted: &MuteFun,
) -> Option<NotesHolderResult> {
match self {
NoteAction::Reply(note_id) => {
@@ -68,28 +66,13 @@ impl NoteAction {
None
}
NoteAction::OpenThread(note_id) => open_thread(
ndb,
txn,
router,
note_cache,
pool,
threads,
note_id.bytes(),
is_muted,
),
NoteAction::OpenThread(note_id) => {
open_thread(ndb, txn, router, note_cache, pool, threads, note_id.bytes())
}
NoteAction::OpenProfile(pubkey) => {
router.route_to(Route::profile(pubkey));
Profile::open(
ndb,
note_cache,
txn,
pool,
profiles,
pubkey.bytes(),
is_muted,
)
Profile::open(ndb, note_cache, txn, pool, profiles, pubkey.bytes())
}
NoteAction::Quote(note_id) => {
@@ -111,13 +94,10 @@ impl NoteAction {
note_cache: &mut NoteCache,
pool: &mut RelayPool,
txn: &Transaction,
is_muted: &MuteFun,
) {
let router = columns.column_mut(col).router_mut();
if let Some(br) = self.execute(
ndb, router, threads, profiles, note_cache, pool, txn, is_muted,
) {
br.process(ndb, note_cache, txn, threads, is_muted);
if let Some(br) = self.execute(ndb, router, threads, profiles, note_cache, pool, txn) {
br.process(ndb, note_cache, txn, threads);
}
}
}
@@ -133,13 +113,12 @@ impl NotesHolderResult {
note_cache: &mut NoteCache,
txn: &Transaction,
storage: &mut NotesHolderStorage<N>,
is_muted: &MuteFun,
) {
match self {
// update the thread for next render if we have new notes
NotesHolderResult::NewNotes(new_notes) => {
let holder = storage
.notes_holder_mutated(ndb, note_cache, txn, &new_notes.id, is_muted)
.notes_holder_mutated(ndb, note_cache, txn, &new_notes.id)
.get_ptr();
new_notes.process(holder);
}

View File

@@ -140,7 +140,6 @@ fn try_process_event(
app_ctx.pool,
app_ctx.note_cache,
timeline,
&app_ctx.accounts.mutefun(),
app_ctx
.accounts
.get_selected_account()
@@ -159,7 +158,6 @@ fn try_process_event(
&txn,
app_ctx.unknown_ids,
app_ctx.note_cache,
&app_ctx.accounts.mutefun(),
) {
error!("poll_notes_into_view: {err}");
}
@@ -200,7 +198,6 @@ fn update_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>, ctx: &egui::Con
app_ctx.ndb,
app_ctx.note_cache,
&mut damus.decks_cache,
&app_ctx.accounts.mutefun(),
) {
warn!("update_damus init: {err}");
}

View File

@@ -4,7 +4,7 @@ use tracing::{debug, error, info};
use uuid::Uuid;
use crate::Error;
use notedeck::{MuteFun, NoteRef, UnifiedSubscription};
use notedeck::{NoteRef, UnifiedSubscription};
pub struct MultiSubscriber {
filters: Vec<Filter>,
@@ -106,12 +106,7 @@ impl MultiSubscriber {
}
}
pub fn poll_for_notes(
&mut self,
ndb: &Ndb,
txn: &Transaction,
is_muted: &MuteFun,
) -> Result<Vec<NoteRef>, Error> {
pub fn poll_for_notes(&mut self, ndb: &Ndb, txn: &Transaction) -> Result<Vec<NoteRef>, Error> {
let sub = self.sub.as_ref().ok_or(notedeck::Error::no_active_sub())?;
let new_note_keys = ndb.poll_for_notes(sub.local, 500);
@@ -129,10 +124,6 @@ impl MultiSubscriber {
continue;
};
if is_muted(&note) {
continue;
}
notes.push(note);
}

View File

@@ -164,7 +164,6 @@ impl RenderNavResponse {
ctx.note_cache,
ctx.pool,
&txn,
&ctx.accounts.mutefun(),
);
}
@@ -209,7 +208,6 @@ impl RenderNavResponse {
&mut app.threads,
ctx.pool,
root_id,
&ctx.accounts.mutefun(),
);
}
@@ -221,7 +219,6 @@ impl RenderNavResponse {
&mut app.profiles,
ctx.pool,
pubkey.bytes(),
&ctx.accounts.mutefun(),
);
}

View File

@@ -2,7 +2,7 @@ use std::collections::HashMap;
use enostr::{Filter, RelayPool};
use nostrdb::{Ndb, Transaction};
use notedeck::{MuteFun, NoteCache, NoteRef, NoteRefsUnkIdAction};
use notedeck::{NoteCache, NoteRef, NoteRefsUnkIdAction};
use tracing::{debug, info, warn};
use crate::{
@@ -56,7 +56,6 @@ impl<M: NotesHolder> NotesHolderStorage<M> {
note_cache: &mut NoteCache,
txn: &Transaction,
id: &[u8; 32],
is_muted: &MuteFun,
) -> Vitality<'a, M> {
// we can't use the naive hashmap entry API here because lookups
// require a copy, wait until we have a raw entry api. We could
@@ -90,7 +89,7 @@ impl<M: NotesHolder> NotesHolderStorage<M> {
self.id_to_object.insert(
id.to_owned(),
M::new_notes_holder(txn, ndb, note_cache, id, M::filters(id), notes, is_muted),
M::new_notes_holder(txn, ndb, note_cache, id, M::filters(id), notes),
);
Vitality::Fresh(self.id_to_object.get_mut(id).unwrap())
}
@@ -109,7 +108,6 @@ pub trait NotesHolder {
id: &[u8; 32],
filters: Vec<Filter>,
notes: Vec<NoteRef>,
is_muted: &MuteFun,
) -> Self;
#[must_use = "process_action must be handled in the Ok(action) case"]
@@ -117,11 +115,10 @@ pub trait NotesHolder {
&mut self,
txn: &Transaction,
ndb: &Ndb,
is_muted: &MuteFun,
) -> Result<NoteRefsUnkIdAction> {
if let Some(multi_subscriber) = self.get_multi_subscriber() {
let reversed = true;
let note_refs: Vec<NoteRef> = multi_subscriber.poll_for_notes(ndb, txn, is_muted)?;
let note_refs: Vec<NoteRef> = multi_subscriber.poll_for_notes(ndb, txn)?;
self.get_view().insert(&note_refs, reversed);
Ok(NoteRefsUnkIdAction::new(note_refs))
} else {
@@ -160,10 +157,9 @@ pub trait NotesHolder {
notes_holder_storage: &mut NotesHolderStorage<M>,
pool: &mut RelayPool,
id: &[u8; 32],
is_muted: &MuteFun,
) {
let notes_holder = notes_holder_storage
.notes_holder_mutated(ndb, note_cache, txn, id, is_muted)
.notes_holder_mutated(ndb, note_cache, txn, id)
.get_ptr();
if let Some(multi_subscriber) = notes_holder.get_multi_subscriber() {
@@ -178,9 +174,8 @@ pub trait NotesHolder {
pool: &mut RelayPool,
storage: &mut NotesHolderStorage<M>,
id: &[u8; 32],
is_muted: &MuteFun,
) -> Option<NotesHolderResult> {
let vitality = storage.notes_holder_mutated(ndb, note_cache, txn, id, is_muted);
let vitality = storage.notes_holder_mutated(ndb, note_cache, txn, id);
let (holder, result) = match vitality {
Vitality::Stale(holder) => {

View File

@@ -5,7 +5,7 @@ use nostrdb::{
FilterBuilder, Ndb, Note, NoteBuildOptions, NoteBuilder, ProfileRecord, Transaction,
};
use notedeck::{filter::default_limit, FilterState, MuteFun, NoteCache, NoteRef};
use notedeck::{filter::default_limit, FilterState, NoteCache, NoteRef};
use tracing::info;
use crate::{
@@ -91,7 +91,6 @@ impl Profile {
source: PubkeySource,
filters: Vec<Filter>,
notes: Vec<NoteRef>,
is_muted: &MuteFun,
) -> Self {
let mut timeline = Timeline::new(
TimelineKind::profile(source),
@@ -99,7 +98,7 @@ impl Profile {
TimelineTab::full_tabs(),
);
copy_notes_into_timeline(&mut timeline, txn, ndb, note_cache, notes, is_muted);
copy_notes_into_timeline(&mut timeline, txn, ndb, note_cache, notes);
Profile {
timeline,
@@ -145,7 +144,6 @@ impl NotesHolder for Profile {
id: &[u8; 32],
filters: Vec<Filter>,
notes: Vec<NoteRef>,
is_muted: &MuteFun,
) -> Self {
Profile::new(
txn,
@@ -154,7 +152,6 @@ impl NotesHolder for Profile {
PubkeySource::Explicit(Pubkey::new(*id)),
filters,
notes,
is_muted,
)
}

View File

@@ -5,7 +5,7 @@ use crate::{
};
use nostrdb::{Filter, FilterBuilder, Ndb, Transaction};
use notedeck::{MuteFun, NoteCache, NoteRef};
use notedeck::{NoteCache, NoteRef};
#[derive(Default)]
pub struct Thread {
@@ -74,7 +74,6 @@ impl NotesHolder for Thread {
_: &[u8; 32],
_: Vec<Filter>,
notes: Vec<NoteRef>,
_: &MuteFun,
) -> Self {
Thread::new(notes)
}

View File

@@ -7,8 +7,7 @@ use crate::{
};
use notedeck::{
filter, CachedNote, FilterError, FilterState, FilterStates, MuteFun, NoteCache, NoteRef,
UnknownIds,
filter, CachedNote, FilterError, FilterState, FilterStates, NoteCache, NoteRef, UnknownIds,
};
use std::fmt;
@@ -287,7 +286,6 @@ impl Timeline {
txn: &Transaction,
unknown_ids: &mut UnknownIds,
note_cache: &mut NoteCache,
is_muted: &MuteFun,
) -> Result<()> {
let timeline = timelines
.get_mut(timeline_idx)
@@ -312,9 +310,6 @@ impl Timeline {
error!("hit race condition in poll_notes_into_view: https://github.com/damus-io/nostrdb/issues/35 note {:?} was not added to timeline", key);
continue;
};
if is_muted(&note) {
continue;
}
UnknownIds::update_from_note(txn, ndb, unknown_ids, note_cache, &note);
@@ -412,12 +407,11 @@ pub fn setup_new_timeline(
pool: &mut RelayPool,
note_cache: &mut NoteCache,
since_optimize: bool,
is_muted: &MuteFun,
our_pk: Option<&Pubkey>,
) {
// if we're ready, setup local subs
if is_timeline_ready(ndb, pool, note_cache, timeline, is_muted, our_pk) {
if let Err(err) = setup_timeline_nostrdb_sub(ndb, note_cache, timeline, is_muted) {
if is_timeline_ready(ndb, pool, note_cache, timeline, our_pk) {
if let Err(err) = setup_timeline_nostrdb_sub(ndb, note_cache, timeline) {
error!("setup_new_timeline: {err}");
}
}
@@ -547,7 +541,6 @@ fn setup_initial_timeline(
timeline: &mut Timeline,
note_cache: &mut NoteCache,
filters: &[Filter],
is_muted: &MuteFun,
) -> Result<()> {
timeline.subscription = Some(ndb.subscribe(filters)?);
let txn = Transaction::new(ndb)?;
@@ -562,7 +555,7 @@ fn setup_initial_timeline(
.map(NoteRef::from_query_result)
.collect();
copy_notes_into_timeline(timeline, &txn, ndb, note_cache, notes, is_muted);
copy_notes_into_timeline(timeline, &txn, ndb, note_cache, notes);
Ok(())
}
@@ -573,7 +566,6 @@ pub fn copy_notes_into_timeline(
ndb: &Ndb,
note_cache: &mut NoteCache,
notes: Vec<NoteRef>,
is_muted: &MuteFun,
) {
let filters = {
let views = &timeline.views;
@@ -585,9 +577,6 @@ pub fn copy_notes_into_timeline(
for note_ref in notes {
for (view, filter) in filters.iter().enumerate() {
if let Ok(note) = ndb.get_note_by_key(txn, note_ref.key) {
if is_muted(&note) {
continue;
}
if filter(
note_cache.cached_note_or_insert_mut(note_ref.key, &note),
&note,
@@ -603,12 +592,11 @@ pub fn setup_initial_nostrdb_subs(
ndb: &Ndb,
note_cache: &mut NoteCache,
decks_cache: &mut DecksCache,
is_muted: &MuteFun,
) -> Result<()> {
for decks in decks_cache.get_all_decks_mut() {
for deck in decks.decks_mut() {
for timeline in deck.columns_mut().timelines_mut() {
if let Err(err) = setup_timeline_nostrdb_sub(ndb, note_cache, timeline, is_muted) {
if let Err(err) = setup_timeline_nostrdb_sub(ndb, note_cache, timeline) {
error!("setup_initial_nostrdb_subs: {err}");
}
}
@@ -622,7 +610,6 @@ fn setup_timeline_nostrdb_sub(
ndb: &Ndb,
note_cache: &mut NoteCache,
timeline: &mut Timeline,
is_muted: &MuteFun,
) -> Result<()> {
let filter_state = timeline
.filter
@@ -630,7 +617,7 @@ fn setup_timeline_nostrdb_sub(
.ok_or(Error::App(notedeck::Error::empty_contact_list()))?
.to_owned();
setup_initial_timeline(ndb, timeline, note_cache, &filter_state, is_muted)?;
setup_initial_timeline(ndb, timeline, note_cache, &filter_state)?;
Ok(())
}
@@ -644,7 +631,6 @@ pub fn is_timeline_ready(
pool: &mut RelayPool,
note_cache: &mut NoteCache,
timeline: &mut Timeline,
is_muted: &MuteFun,
our_pk: Option<&Pubkey>,
) -> bool {
// TODO: we should debounce the filter states a bit to make sure we have
@@ -705,8 +691,7 @@ pub fn is_timeline_ready(
// we just switched to the ready state, we should send initial
// queries and setup the local subscription
info!("Found contact list! Setting up local and remote contact list query");
setup_initial_timeline(ndb, timeline, note_cache, &filter, is_muted)
.expect("setup init");
setup_initial_timeline(ndb, timeline, note_cache, &filter).expect("setup init");
timeline
.filter
.set_relay_state(relay_id, FilterState::ready(filter.clone()));

View File

@@ -63,6 +63,7 @@ pub fn render_timeline_route(
note_cache,
img_cache,
note_options,
&accounts.mutefun(),
)
.ui(ui);
@@ -77,9 +78,10 @@ pub fn render_timeline_route(
img_cache,
id.bytes(),
textmode,
&accounts.mutefun(),
)
.id_source(egui::Id::new(("threadscroll", col)))
.ui(ui, &accounts.mutefun())
.ui(ui)
.map(Into::into),
TimelineRoute::Reply(id) => {
@@ -173,9 +175,10 @@ pub fn render_profile_route(
ndb,
note_cache,
img_cache,
is_muted,
NoteOptions::default(),
)
.ui(ui, is_muted);
.ui(ui);
if let Some(action) = action {
match action {

View File

@@ -503,7 +503,6 @@ pub fn render_add_column_routes(
ctx.pool,
ctx.note_cache,
app.since_optimize,
&ctx.accounts.mutefun(),
ctx.accounts
.get_selected_account()
.as_ref()

View File

@@ -29,6 +29,7 @@ pub struct ProfileView<'a> {
ndb: &'a Ndb,
note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache,
is_muted: &'a MuteFun,
}
pub enum ProfileViewAction {
@@ -46,6 +47,7 @@ impl<'a> ProfileView<'a> {
ndb: &'a Ndb,
note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache,
is_muted: &'a MuteFun,
note_options: NoteOptions,
) -> Self {
ProfileView {
@@ -57,10 +59,11 @@ impl<'a> ProfileView<'a> {
note_cache,
img_cache,
note_options,
is_muted,
}
}
pub fn ui(&mut self, ui: &mut egui::Ui, is_muted: &MuteFun) -> Option<ProfileViewAction> {
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<ProfileViewAction> {
let scroll_id = egui::Id::new(("profile_scroll", self.col_id, self.pubkey));
ScrollArea::vertical()
@@ -75,20 +78,14 @@ impl<'a> ProfileView<'a> {
}
let profile = self
.profiles
.notes_holder_mutated(
self.ndb,
self.note_cache,
&txn,
self.pubkey.bytes(),
is_muted,
)
.notes_holder_mutated(self.ndb, self.note_cache, &txn, self.pubkey.bytes())
.get_ptr();
profile.timeline.selected_view =
tabs_ui(ui, profile.timeline.selected_view, &profile.timeline.views);
// poll for new notes and insert them into our existing notes
if let Err(e) = profile.poll_notes_into_view(&txn, self.ndb, is_muted) {
if let Err(e) = profile.poll_notes_into_view(&txn, self.ndb) {
error!("Profile::poll_notes_into_view: {e}");
}
@@ -102,11 +99,13 @@ impl<'a> ProfileView<'a> {
self.ndb,
self.note_cache,
self.img_cache,
self.is_muted,
)
.show(ui)
{
action = Some(ProfileViewAction::Note(note_action));
}
action
})
.inner

View File

@@ -20,6 +20,7 @@ pub struct ThreadView<'a> {
selected_note_id: &'a [u8; 32],
textmode: bool,
id_source: egui::Id,
is_muted: &'a MuteFun,
}
impl<'a> ThreadView<'a> {
@@ -32,6 +33,7 @@ impl<'a> ThreadView<'a> {
img_cache: &'a mut ImageCache,
selected_note_id: &'a [u8; 32],
textmode: bool,
is_muted: &'a MuteFun,
) -> Self {
let id_source = egui::Id::new("threadscroll_threadview");
ThreadView {
@@ -43,6 +45,7 @@ impl<'a> ThreadView<'a> {
selected_note_id,
textmode,
id_source,
is_muted,
}
}
@@ -51,7 +54,7 @@ impl<'a> ThreadView<'a> {
self
}
pub fn ui(&mut self, ui: &mut egui::Ui, is_muted: &MuteFun) -> Option<NoteAction> {
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<NoteAction> {
let txn = Transaction::new(self.ndb).expect("txn");
let selected_note_key =
@@ -93,13 +96,13 @@ impl<'a> ThreadView<'a> {
let thread = self
.threads
.notes_holder_mutated(self.ndb, self.note_cache, &txn, root_id, is_muted)
.notes_holder_mutated(self.ndb, self.note_cache, &txn, root_id)
.get_ptr();
// TODO(jb55): skip poll if ThreadResult is fresh?
// poll for new notes and insert them into our existing notes
match thread.poll_notes_into_view(&txn, self.ndb, is_muted) {
match thread.poll_notes_into_view(&txn, self.ndb) {
Ok(action) => {
action.process_action(&txn, self.ndb, self.unknown_ids, self.note_cache)
}
@@ -119,6 +122,7 @@ impl<'a> ThreadView<'a> {
self.ndb,
self.note_cache,
self.img_cache,
self.is_muted,
)
.show(ui)
})

View File

@@ -7,10 +7,11 @@ use crate::{
ui::note::NoteOptions,
};
use egui::containers::scroll_area::ScrollBarVisibility;
use egui::{Direction, Layout};
use egui::{Color32, Direction, Layout};
use egui_tabs::TabColor;
use nostrdb::{Ndb, Transaction};
use notedeck::{ImageCache, NoteCache};
use notedeck::note::root_note_id_from_selected_id;
use notedeck::{ImageCache, MuteFun, NoteCache};
use tracing::{error, warn};
pub struct TimelineView<'a> {
@@ -21,6 +22,7 @@ pub struct TimelineView<'a> {
img_cache: &'a mut ImageCache,
note_options: NoteOptions,
reverse: bool,
is_muted: &'a MuteFun,
}
impl<'a> TimelineView<'a> {
@@ -31,6 +33,7 @@ impl<'a> TimelineView<'a> {
note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache,
note_options: NoteOptions,
is_muted: &'a MuteFun,
) -> TimelineView<'a> {
let reverse = false;
TimelineView {
@@ -41,6 +44,7 @@ impl<'a> TimelineView<'a> {
img_cache,
reverse,
note_options,
is_muted,
}
}
@@ -54,6 +58,7 @@ impl<'a> TimelineView<'a> {
self.img_cache,
self.reverse,
self.note_options,
self.is_muted,
)
}
@@ -73,6 +78,7 @@ fn timeline_ui(
img_cache: &mut ImageCache,
reversed: bool,
note_options: NoteOptions,
is_muted: &MuteFun,
) -> Option<NoteAction> {
//padding(4.0, ui, |ui| ui.heading("Notifications"));
/*
@@ -123,6 +129,7 @@ fn timeline_ui(
ndb,
note_cache,
img_cache,
is_muted,
)
.show(ui)
})
@@ -224,9 +231,11 @@ pub struct TimelineTabView<'a> {
ndb: &'a Ndb,
note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache,
is_muted: &'a MuteFun,
}
impl<'a> TimelineTabView<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
tab: &'a TimelineTab,
reversed: bool,
@@ -235,6 +244,7 @@ impl<'a> TimelineTabView<'a> {
ndb: &'a Ndb,
note_cache: &'a mut NoteCache,
img_cache: &'a mut ImageCache,
is_muted: &'a MuteFun,
) -> Self {
Self {
tab,
@@ -244,6 +254,7 @@ impl<'a> TimelineTabView<'a> {
ndb,
note_cache,
img_cache,
is_muted,
}
}
@@ -251,6 +262,7 @@ impl<'a> TimelineTabView<'a> {
let mut action: Option<NoteAction> = None;
let len = self.tab.notes.len();
let is_muted = self.is_muted;
self.tab
.list
.clone()
@@ -275,17 +287,30 @@ impl<'a> TimelineTabView<'a> {
};
ui::padding(8.0, ui, |ui| {
let resp = ui::NoteView::new(self.ndb, self.note_cache, self.img_cache, &note)
.note_options(self.note_options)
.show(ui);
if let Some(muted_reason) = is_muted(
&note,
root_note_id_from_selected_id(
self.ndb,
self.note_cache,
self.txn,
note.id(),
),
) {
ui.colored_label(Color32::RED, format!("MUTED {}", muted_reason));
} else {
let resp =
ui::NoteView::new(self.ndb, self.note_cache, self.img_cache, &note)
.note_options(self.note_options)
.show(ui);
if let Some(note_action) = resp.action {
action = Some(note_action)
}
if let Some(note_action) = resp.action {
action = Some(note_action)
}
if let Some(context) = resp.context_selection {
context.process(ui, &note);
}
if let Some(context) = resp.context_selection {
context.process(ui, &note);
}
};
});
ui::hline(ui);