Move profile update handling from notes to the background

During profiling, I found that some large hangs were being caused by a
large number of `notify` calls (and their handling functions) keeping
the main thread overly busy.

We cannot move the `notify` mechanism to a background thread (It has to
be done on the main actor or else runtime warnings/errors appear), so
instead this commit removes a very large source of notify calls/handling around
NoteContentView, and replaces it with a background task that streams
for profile updates and only updates its view when a relevant profile is
updated.

Changelog-Changed: Improved performance around note content views to prevent hangs
Closes: https://github.com/damus-io/damus/issues/3439
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
Daniel D’Aquino
2025-12-10 19:20:05 -08:00
parent d3a54458f5
commit 48143f859a
9 changed files with 90 additions and 140 deletions

View File

@@ -101,10 +101,17 @@ extension NostrNetworkManager {
relevantStream.continuation.yield(profile)
}
}
// Notify the rest of the app so views that rely on rendered text (like mention strings)
// can reload and pick up the freshly fetched profile metadata.
notify(.profile_updated(.remote(pubkey: metadataEvent.pubkey)))
}
/// Manually trigger profile updates for a given pubkey
/// This is useful for local profile changes (e.g., nip05 validation, donation percentage updates)
func notifyProfileUpdate(pubkey: Pubkey) {
if let relevantStreams = streams[pubkey] {
guard let profile = ndb.lookup_profile_and_copy(pubkey) else { return }
for relevantStream in relevantStreams.values {
relevantStream.continuation.yield(profile)
}
}
}
@@ -121,6 +128,29 @@ extension NostrNetworkManager {
}
}
func streamProfiles(pubkeys: Set<Pubkey>) -> AsyncStream<ProfileStreamItem> {
guard !pubkeys.isEmpty else {
return AsyncStream<ProfileStreamItem> { continuation in
continuation.finish()
}
}
return AsyncStream<ProfileStreamItem> { continuation in
let stream = ProfileStreamInfo(continuation: continuation)
for pubkey in pubkeys {
self.add(pubkey: pubkey, stream: stream)
}
continuation.onTermination = { @Sendable _ in
Task {
for pubkey in pubkeys {
await self.removeStream(pubkey: pubkey, id: stream.id)
}
}
}
}
}
// MARK: - Stream management