[PATCH damus v2 0/2] Mute reposts of muted events

0 views
Skip to first unread message

Daniel D’Aquino

unread,
May 24, 2024, 6:58:51 PMMay 24
to pat...@damus.io, Daniel D’Aquino
Hi Will,

Here is the updated patch series for muting reposts of muted events.


PATCH CHANGELOG:
- V1: Initial implementation using `get_inner_event()`
- V2: Use `ndb` queries to get the inner event for improved efficiency

Notes:
- I did not use `get_inner_event(cache: EventCache)` because that would require substantial refactoring (We would need to get EventCache and its dependencies to be included in the notification extension target, which I remember was a substantial amount of dependencies)
- I did not add depth limits to the muting computations, because it is not cryptographically possible for a note to refer to itself by through note IDs, as those are hashes of its own contents. Therefore, we can safely assume that the depth of reposts is finite.
- I retested changes to make sure they still work as expected

Please let me know if you have any questions or concerns.

Best regards,
Daniel

Daniel D’Aquino (2):
Add ndb to MutelistManager
Mute reposts of muted events

.../NotificationExtensionState.swift | 2 +-
damus/ContentView.swift | 2 +-
damus/Models/DamusState.swift | 2 +-
damus/Models/MutelistManager.swift | 9 ++++++++-
damus/TestData.swift | 2 +-
damusTests/Mocking/MockDamusState.swift | 2 +-
nostrdb/NdbNote.swift | 13 +++++++++++++
7 files changed, 26 insertions(+), 6 deletions(-)


base-commit: 0d9954290a674e1520164c08050bcfb9291fdd05
--
2.44.0


Daniel D’Aquino

unread,
May 24, 2024, 6:59:01 PMMay 24
to pat...@damus.io, Daniel D’Aquino
Signed-off-by: Daniel D’Aquino <dan...@daquino.me>
---
DamusNotificationService/NotificationExtensionState.swift | 2 +-
damus/ContentView.swift | 2 +-
damus/Models/DamusState.swift | 2 +-
damus/Models/MutelistManager.swift | 4 +++-
damus/TestData.swift | 2 +-
damusTests/Mocking/MockDamusState.swift | 2 +-
6 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/DamusNotificationService/NotificationExtensionState.swift b/DamusNotificationService/NotificationExtensionState.swift
index 0cfb8e6a..f7daa5b5 100644
--- a/DamusNotificationService/NotificationExtensionState.swift
+++ b/DamusNotificationService/NotificationExtensionState.swift
@@ -28,7 +28,7 @@ struct NotificationExtensionState: HeadlessDamusState {
self.settings = UserSettingsStore()

self.contacts = Contacts(our_pubkey: keypair.pubkey)
- self.mutelist_manager = MutelistManager(user_keypair: keypair)
+ self.mutelist_manager = MutelistManager(user_keypair: keypair, ndb: self.ndb)
self.keypair = keypair
self.profiles = Profiles(ndb: ndb)
self.zaps = Zaps(our_pubkey: keypair.pubkey)
diff --git a/damus/ContentView.swift b/damus/ContentView.swift
index b3846a57..a70bf421 100644
--- a/damus/ContentView.swift
+++ b/damus/ContentView.swift
@@ -699,7 +699,7 @@ struct ContentView: View {
likes: EventCounter(our_pubkey: pubkey),
boosts: EventCounter(our_pubkey: pubkey),
contacts: Contacts(our_pubkey: pubkey),
- mutelist_manager: MutelistManager(user_keypair: keypair),
+ mutelist_manager: MutelistManager(user_keypair: keypair, ndb: ndb),
profiles: Profiles(ndb: ndb),
dms: home.dms,
previews: PreviewCache(),
diff --git a/damus/Models/DamusState.swift b/damus/Models/DamusState.swift
index 13679e45..4cc7de80 100644
--- a/damus/Models/DamusState.swift
+++ b/damus/Models/DamusState.swift
@@ -112,7 +112,7 @@ class DamusState: HeadlessDamusState {
likes: EventCounter(our_pubkey: empty_pub),
boosts: EventCounter(our_pubkey: empty_pub),
contacts: Contacts(our_pubkey: empty_pub),
- mutelist_manager: MutelistManager(user_keypair: kp),
+ mutelist_manager: MutelistManager(user_keypair: kp, ndb: .empty),
profiles: Profiles(ndb: .empty),
dms: DirectMessagesModel(our_pubkey: empty_pub),
previews: PreviewCache(),
diff --git a/damus/Models/MutelistManager.swift b/damus/Models/MutelistManager.swift
index 1096bdf9..8a444b0a 100644
--- a/damus/Models/MutelistManager.swift
+++ b/damus/Models/MutelistManager.swift
@@ -9,6 +9,7 @@ import Foundation

class MutelistManager {
let user_keypair: Keypair
+ let ndb: Ndb
private(set) var event: NostrEvent? = nil

var users: Set<MuteItem> = [] {
@@ -26,8 +27,9 @@ class MutelistManager {

var muted_notes_cache: [NoteId: EventMuteStatus] = [:]

- init(user_keypair: Keypair) {
+ init(user_keypair: Keypair, ndb: Ndb) {
self.user_keypair = user_keypair
+ self.ndb = ndb
}

func refresh_sets() {
diff --git a/damus/TestData.swift b/damus/TestData.swift
index d7e9c977..8b931ef2 100644
--- a/damus/TestData.swift
+++ b/damus/TestData.swift
@@ -73,7 +73,7 @@ var test_damus_state: DamusState = ({
likes: .init(our_pubkey: our_pubkey),
boosts: .init(our_pubkey: our_pubkey),
contacts: .init(our_pubkey: our_pubkey),
- mutelist_manager: MutelistManager(user_keypair: test_keypair),
+ mutelist_manager: MutelistManager(user_keypair: test_keypair, ndb: ndb),
profiles: .init(ndb: ndb),
dms: .init(our_pubkey: our_pubkey),
previews: .init(),
diff --git a/damusTests/Mocking/MockDamusState.swift b/damusTests/Mocking/MockDamusState.swift
index e554f2fe..fe1094dd 100644
--- a/damusTests/Mocking/MockDamusState.swift
+++ b/damusTests/Mocking/MockDamusState.swift
@@ -25,7 +25,7 @@ func generate_test_damus_state(
return profiles
}()

- let mutelist_manager = MutelistManager(user_keypair: test_keypair)
+ let mutelist_manager = MutelistManager(user_keypair: test_keypair, ndb: ndb)
let damus = DamusState(pool: pool,
keypair: test_keypair,
likes: .init(our_pubkey: our_pubkey),
--
2.44.0


Daniel D’Aquino

unread,
May 24, 2024, 6:59:07 PMMay 24
to pat...@damus.io, Daniel D’Aquino
Testing
--------

PASS

Device: iPhone 15 simulator
iOS: 17.4
Damus: This commit
Steps:
1. Mute a specific user "A" from account "B"
2. Make account "B" follow account "C"
3. Using a separate account "C", repost a note from account "A"
4. Make sure that the reposted note from step 3 does not appear on account "B"'s timeline
5. Make sure other reposts of other users still show normally

Closes: https://github.com/damus-io/damus/issues/1934
Changelog-Changed: Do not show reposts of muted events
Signed-off-by: Daniel D’Aquino <dan...@daquino.me>
---
damus/Models/MutelistManager.swift | 5 +++++
nostrdb/NdbNote.swift | 13 +++++++++++++
2 files changed, 18 insertions(+)

diff --git a/damus/Models/MutelistManager.swift b/damus/Models/MutelistManager.swift
index 8a444b0a..270fc723 100644
--- a/damus/Models/MutelistManager.swift
+++ b/damus/Models/MutelistManager.swift
@@ -189,6 +189,11 @@ class MutelistManager {
}
}
}
+
+ // Check if event is a repost of a muted event
+ if let inner_event = ev.get_inner_event(ndb: self.ndb) {
+ return self.compute_event_muted_reason(inner_event)
+ }

return nil
}
diff --git a/nostrdb/NdbNote.swift b/nostrdb/NdbNote.swift
index ce2f358f..445196e2 100644
--- a/nostrdb/NdbNote.swift
+++ b/nostrdb/NdbNote.swift
@@ -272,6 +272,19 @@ class NdbNote: Encodable, Equatable, Hashable {
func get_inner_event() -> NdbNote? {
return self.inner_event
}
+
+ func get_inner_event(ndb: Ndb) -> NdbNote? {
+ guard self.known_kind == .boost else {
+ return nil
+ }
+
+ if let id = self.referenced_ids.first {
+ guard let note_key = ndb.lookup_note_key(id) else { return nil }
+ return ndb.lookup_note_by_key(note_key)?.unsafeUnownedValue?.to_owned()
+ }
+
+ return nil
+ }
}

// Extension to make NdbNote compatible with NostrEvent's original API
--
2.44.0


William Casarin

unread,
May 24, 2024, 8:54:34 PMMay 24
to Daniel D’Aquino, pat...@damus.io
On Fri, May 24, 2024 at 10:58:59PM GMT, Daniel D’Aquino wrote:
>Testing
>--------
>
>PASS
>
>Device: iPhone 15 simulator
>iOS: 17.4
>Damus: This commit
>Steps:
>1. Mute a specific user "A" from account "B"
>2. Make account "B" follow account "C"
>3. Using a separate account "C", repost a note from account "A"
>4. Make sure that the reposted note from step 3 does not appear on account "B"'s timeline
>5. Make sure other reposts of other users still show normally
>
>Closes: https://github.com/damus-io/damus/issues/1934
>Changelog-Changed: Do not show reposts of muted events
>Signed-off-by: Daniel D’Aquino <dan...@daquino.me>
>---
> damus/Models/MutelistManager.swift | 5 +++++
> nostrdb/NdbNote.swift | 13 +++++++++++++
> 2 files changed, 18 insertions(+)
>
> [..]
>
>diff --git a/nostrdb/NdbNote.swift b/nostrdb/NdbNote.swift
>index ce2f358f..445196e2 100644
>--- a/nostrdb/NdbNote.swift
>+++ b/nostrdb/NdbNote.swift
>@@ -272,6 +272,19 @@ class NdbNote: Encodable, Equatable, Hashable {
> func get_inner_event() -> NdbNote? {
> return self.inner_event
> }
>+
>+ func get_inner_event(ndb: Ndb) -> NdbNote? {
>+ guard self.known_kind == .boost else {
>+ return nil
>+ }
>+
>+ if let id = self.referenced_ids.first {
>+ guard let note_key = ndb.lookup_note_key(id) else { return nil }
>+ return ndb.lookup_note_by_key(note_key)?.unsafeUnownedValue?.to_owned()
>+ }

my only concern here is that this is pretty inefficient, ideally if we
are copying notes into memory it would not be a one-off things.

Since this is a temporary, instead of doing to_owned we can just return
NdbTxn<NdbNote>. This way we won't need a copy and you can refer to the
n ote data within nostrdb:

func get_inner_event(ndb: Ndb) -> NdbTxn<NdbNote>? {
guard self.known_kind == .boost else {
return nil
}

if let id = self.referenced_ids.first {
guard let note_key = ndb.lookup_note_key(id) else { return nil }
return ndb.lookup_note_by_key(note_key).collect()
}

return nil
}

// ...

if let inner_event = ev.get_inner_event(ndb: self.ndb) {
return self.compute_event_muted_reason(inner_event.unsafeUnownedValue)
}

Daniel D'Aquino

unread,
May 24, 2024, 9:10:29 PMMay 24
to William Casarin, pat...@damus.io
I see! v3 coming up soon!

Daniel D'Aquino

unread,
May 24, 2024, 9:22:52 PMMay 24
to William Casarin, sembene_truestar via patches, dev
On May 24, 2024, at 18:09, Daniel D'Aquino <dan...@daquino.me> wrote:

On May 24, 2024, at 16:54, William Casarin <jb...@jb55.com> wrote:

I see! v3 coming up soon!


NostrDB question. NostrDB transactions keep memory pages open and prevent them from being overwritten unexpectedly by concurrently executing code. However, if we pass around that NdbTxn<NdbNote> object, does it guarantee the transaction will remain open and thus our NdbNote will be there?

In other words, if we write some code in Swift that keeps the reference count to the NdbTxn object above zero (preventing NdbTxn from being garbage collected by Swift), is it safe to access the underlying NdbNote?

William Casarin

unread,
May 27, 2024, 7:44:45 PMMay 27
to Daniel D'Aquino, pat...@damus.io, dev
On Sat, May 25, 2024 at 01:22:45AM GMT, Daniel D'Aquino wrote:
>
>NostrDB question. NostrDB transactions keep memory pages open and
>prevent them from being overwritten unexpectedly by concurrently
>executing code. However, if we pass around that NdbTxn<NdbNote> object,
>does it guarantee the transaction will remain open and thus our NdbNote
>will be there?
>
>In other words, if we write some code in Swift that keeps the reference
>count to the NdbTxn object above zero (preventing NdbTxn from being
>garbage collected by Swift), is it safe to access the underlying
>NdbNote?

yeah NdbTxn opens a transaction in its constructor and closes that
transaction in its destructor. So as long as its in scope the data it
refers to will be valid.

These should never be stored though, as long running transactions will
keep holding onto pages forever which is bad.
Reply all
Reply to author
Forward
0 new messages