From de528f3f70ebbbbf7664ae30ba48e1383d3d3bb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=E2=80=99Aquino?= Date: Wed, 17 Sep 2025 12:29:11 -0700 Subject: [PATCH] Improve loading speed on home timeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit improves the loading speed for the home timeline (and likely other areas of the app) by employing various techniques and changes: - Network EOSE timeout reduced from 10 seconds down to 5 seconds - Network EOSE does not wait on relays with broken connections - Offload HomeModel handler event processing to separate tasks to avoid a large backlog - Give SubscriptionManager streamers more fine-grained EOSE signals for local optimization - Only wait for Ndb EOSE on the home timeline for faster loading - Add logging with time elapsed measurements for easier identification of loading problems Signed-off-by: Daniel D’Aquino --- .../SubscriptionManager.swift | 65 ++++++++++++++----- .../UserRelayListManager.swift | 2 + damus/Core/Nostr/RelayPool.swift | 11 ++-- damus/Features/Chat/Models/ThreadModel.swift | 4 ++ .../Features/Events/Models/EventsModel.swift | 4 ++ .../FollowPack/Models/FollowPackModel.swift | 4 ++ .../Follows/Models/FollowersModel.swift | 8 +++ .../NIP05/Models/NIP05DomainEventsModel.swift | 4 ++ .../Onboarding/SuggestedUsersViewModel.swift | 48 ++++++-------- .../Profile/Models/ProfileModel.swift | 12 ++++ .../Features/Timeline/Models/HomeModel.swift | 46 ++++++++++++- .../Features/Wallet/Models/WalletModel.swift | 4 ++ damus/Features/Zaps/Models/ZapsModel.swift | 4 ++ damus/Shared/Utilities/Log.swift | 1 + .../NostrNetworkManagerTests.swift | 4 ++ 15 files changed, 171 insertions(+), 50 deletions(-) diff --git a/damus/Core/Networking/NostrNetworkManager/SubscriptionManager.swift b/damus/Core/Networking/NostrNetworkManager/SubscriptionManager.swift index cbbef549..7ffec8e2 100644 --- a/damus/Core/Networking/NostrNetworkManager/SubscriptionManager.swift +++ b/damus/Core/Networking/NostrNetworkManager/SubscriptionManager.swift @@ -18,6 +18,8 @@ extension NostrNetworkManager { private var taskManager: TaskManager private let experimentalLocalRelayModelSupport: Bool + let EXTRA_VERBOSE_LOGGING: Bool = false + init(pool: RelayPool, ndb: Ndb, experimentalLocalRelayModelSupport: Bool) { self.pool = pool self.ndb = ndb @@ -28,17 +30,21 @@ extension NostrNetworkManager { // MARK: - Subscribing and Streaming data from Nostr /// Streams notes until the EOSE signal - func streamNotesUntilEndOfStoredEvents(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, timeout: Duration? = nil) -> AsyncStream { + func streamNotesUntilEndOfStoredEvents(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, timeout: Duration? = nil, id: UUID? = nil) -> AsyncStream { let timeout = timeout ?? .seconds(10) return AsyncStream { continuation in let streamingTask = Task { - outerLoop: for await item in self.subscribe(filters: filters, to: desiredRelays, timeout: timeout) { + outerLoop: for await item in self.subscribe(filters: filters, to: desiredRelays, timeout: timeout, id: id) { try Task.checkCancellation() switch item { case .event(let lender): continuation.yield(lender) case .eose: break outerLoop + case .ndbEose: + continue + case .networkEose: + continue } } continuation.finish() @@ -52,10 +58,10 @@ extension NostrNetworkManager { /// Subscribes to data from user's relays, for a maximum period of time — after which the stream will end. /// /// This is useful when waiting for some specific data from Nostr, but not indefinitely. - func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, timeout: Duration) -> AsyncStream { + func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, timeout: Duration, id: UUID? = nil) -> AsyncStream { return AsyncStream { continuation in let streamingTask = Task { - for await item in self.subscribe(filters: filters, to: desiredRelays) { + for await item in self.subscribe(filters: filters, to: desiredRelays, id: id) { try Task.checkCancellation() continuation.yield(item) } @@ -79,9 +85,10 @@ extension NostrNetworkManager { /// /// - Parameter filters: The nostr filters to specify what kind of data to subscribe to /// - Returns: An async stream of nostr data - func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil) -> AsyncStream { + func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, id: UUID? = nil) -> AsyncStream { return AsyncStream { continuation in - let subscriptionId = UUID() + let subscriptionId = id ?? UUID() + let startTime = CFAbsoluteTimeGetCurrent() Log.info("Starting subscription %s: %s", for: .subscription_manager, subscriptionId.uuidString, filters.debugDescription) let multiSessionStreamingTask = Task { while !Task.isCancelled { @@ -97,7 +104,7 @@ extension NostrNetworkManager { continue } Log.info("%s: Streaming.", for: .subscription_manager, subscriptionId.uuidString) - for await item in self.sessionSubscribe(filters: filters, to: desiredRelays) { + for await item in self.sessionSubscribe(filters: filters, to: desiredRelays, id: id) { try Task.checkCancellation() continuation.yield(item) } @@ -127,8 +134,11 @@ extension NostrNetworkManager { /// /// - Parameter filters: The nostr filters to specify what kind of data to subscribe to /// - Returns: An async stream of nostr data - private func sessionSubscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil) -> AsyncStream { + private func sessionSubscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, id: UUID? = nil) -> AsyncStream { + let id = id ?? UUID() return AsyncStream { continuation in + let startTime = CFAbsoluteTimeGetCurrent() + Log.debug("Session subscription %s: Started", for: .subscription_manager, id.uuidString) var ndbEOSEIssued = false var networkEOSEIssued = false @@ -143,6 +153,7 @@ extension NostrNetworkManager { (ndbEOSEIssued && (networkEOSEIssued || !connectedToNetwork)) if canIssueEOSE { + Log.debug("Session subscription %s: Issued EOSE for session. Elapsed: %.2f seconds", for: .subscription_manager, id.uuidString, CFAbsoluteTimeGetCurrent() - startTime) continuation.yield(.eose) } } @@ -153,7 +164,8 @@ extension NostrNetworkManager { try Task.checkCancellation() switch item { case .eose: - Log.debug("Session subscribe: Received EOSE from nostrdb", for: .subscription_manager) + Log.debug("Session subscription %s: Received EOSE from nostrdb. Elapsed: %.2f seconds", for: .subscription_manager, id.uuidString, CFAbsoluteTimeGetCurrent() - startTime) + continuation.yield(.ndbEose) ndbEOSEIssued = true yieldEOSEIfReady() case .event(let noteKey): @@ -170,32 +182,35 @@ extension NostrNetworkManager { } } catch { - Log.error("NDB streaming error: %s", for: .subscription_manager, error.localizedDescription) + Log.error("Session subscription %s: NDB streaming error: %s", for: .subscription_manager, id.uuidString, error.localizedDescription) } continuation.finish() } let streamTask = Task { do { - for await item in self.pool.subscribe(filters: filters, to: desiredRelays) { + for await item in self.pool.subscribe(filters: filters, to: desiredRelays, id: id) { // NO-OP. Notes will be automatically ingested by NostrDB // TODO: Improve efficiency of subscriptions? try Task.checkCancellation() switch item { case .event(let event): - Log.debug("Session subscribe: Received kind %d event with id %s from the network", for: .subscription_manager, event.kind, event.id.hex()) + if EXTRA_VERBOSE_LOGGING { + Log.debug("Session subscription %s: Received kind %d event with id %s from the network", for: .subscription_manager, id.uuidString, event.kind, event.id.hex()) + } if !self.experimentalLocalRelayModelSupport { // In normal mode (non-experimental), we stream from ndb but also directly from the network continuation.yield(.event(lender: NdbNoteLender(ownedNdbNote: event))) } case .eose: - Log.debug("Session subscribe: Received EOSE from the network", for: .subscription_manager) + Log.debug("Session subscription %s: Received EOSE from the network. Elapsed: %.2f seconds", for: .subscription_manager, id.uuidString, CFAbsoluteTimeGetCurrent() - startTime) + continuation.yield(.networkEose) networkEOSEIssued = true yieldEOSEIfReady() } } } catch { - Log.error("Network streaming error: %s", for: .subscription_manager, error.localizedDescription) + Log.error("Session subscription %s: Network streaming error: %s", for: .subscription_manager, id.uuidString, error.localizedDescription) } continuation.finish() } @@ -348,7 +363,27 @@ extension NostrNetworkManager { enum StreamItem { /// An event which can be borrowed from NostrDB case event(lender: NdbNoteLender) - /// The end of stored events + /// The canonical "end of stored events". See implementations of `subscribe` to see when this event is fired in relation to other EOSEs case eose + /// "End of stored events" from NostrDB. + case ndbEose + /// "End of stored events" from all relays in `RelayPool`. + case networkEose + + var debugDescription: String { + switch self { + case .event(lender: let lender): + let detailedDescription = try? lender.borrow({ event in + "Note with ID: \(event.id.hex())" + }) + return detailedDescription ?? "Some note" + case .eose: + return "EOSE" + case .ndbEose: + return "NDB EOSE" + case .networkEose: + return "NETWORK EOSE" + } + } } } diff --git a/damus/Core/Networking/NostrNetworkManager/UserRelayListManager.swift b/damus/Core/Networking/NostrNetworkManager/UserRelayListManager.swift index 01225fc5..f0ed0da8 100644 --- a/damus/Core/Networking/NostrNetworkManager/UserRelayListManager.swift +++ b/damus/Core/Networking/NostrNetworkManager/UserRelayListManager.swift @@ -145,6 +145,8 @@ extension NostrNetworkManager { try? self.set(userRelayList: relayList) // Set the validated list }) case .eose: continue + case .ndbEose: continue + case .networkEose: continue } } } diff --git a/damus/Core/Nostr/RelayPool.swift b/damus/Core/Nostr/RelayPool.swift index b15eece4..ffa38c0b 100644 --- a/damus/Core/Nostr/RelayPool.swift +++ b/damus/Core/Nostr/RelayPool.swift @@ -232,11 +232,13 @@ class RelayPool { /// - desiredRelays: The desired relays which to subsctibe to. If `nil`, it defaults to the `RelayPool`'s default list /// - eoseTimeout: The maximum timeout which to give up waiting for the eoseSignal /// - Returns: Returns an async stream that callers can easily consume via a for-loop - func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, eoseTimeout: Duration? = nil) -> AsyncStream { - let eoseTimeout = eoseTimeout ?? .seconds(10) - let desiredRelays = desiredRelays ?? self.relays.map({ $0.descriptor.url }) + func subscribe(filters: [NostrFilter], to desiredRelays: [RelayURL]? = nil, eoseTimeout: Duration? = nil, id: UUID? = nil) -> AsyncStream { + let eoseTimeout = eoseTimeout ?? .seconds(5) + let desiredRelays = desiredRelays ?? self.relays.filter({ $0.connection.isConnected }).map({ $0.descriptor.url }) + let startTime = CFAbsoluteTimeGetCurrent() return AsyncStream { continuation in - let sub_id = UUID().uuidString + let id = id ?? UUID() + let sub_id = id.uuidString var seenEvents: Set = [] var relaysWhoFinishedInitialResults: Set = [] var eoseSent = false @@ -257,6 +259,7 @@ class RelayPool { break // We do not support handling these yet case .eose(_): relaysWhoFinishedInitialResults.insert(relayUrl) + Log.debug("RelayPool subscription %s: EOSE from %s. EOSE count: %d/%d. Elapsed: %.2f seconds.", for: .networking, id.uuidString, relayUrl.absoluteString, relaysWhoFinishedInitialResults.count, Set(desiredRelays).count, CFAbsoluteTimeGetCurrent() - startTime) if relaysWhoFinishedInitialResults == Set(desiredRelays) { continuation.yield(with: .success(.eose)) eoseSent = true diff --git a/damus/Features/Chat/Models/ThreadModel.swift b/damus/Features/Chat/Models/ThreadModel.swift index e22ca637..b414b945 100644 --- a/damus/Features/Chat/Models/ThreadModel.swift +++ b/damus/Features/Chat/Models/ThreadModel.swift @@ -122,6 +122,10 @@ class ThreadModel: ObservableObject { case .eose: guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } load_profiles(context: "thread", load: .from_events(Array(event_map.events)), damus_state: damus_state, txn: txn) + case .ndbEose: + break + case .networkEose: + break } } } diff --git a/damus/Features/Events/Models/EventsModel.swift b/damus/Features/Events/Models/EventsModel.swift index 049ecfe7..9a0fd1ea 100644 --- a/damus/Features/Events/Models/EventsModel.swift +++ b/damus/Features/Events/Models/EventsModel.swift @@ -84,6 +84,10 @@ class EventsModel: ObservableObject { case .eose: DispatchQueue.main.async { self.loading = false } break outerLoop + case .ndbEose: + break + case .networkEose: + break } } DispatchQueue.main.async { self.loading = false } diff --git a/damus/Features/FollowPack/Models/FollowPackModel.swift b/damus/Features/FollowPack/Models/FollowPackModel.swift index 7049efa9..80a66d8e 100644 --- a/damus/Features/FollowPack/Models/FollowPackModel.swift +++ b/damus/Features/FollowPack/Models/FollowPackModel.swift @@ -59,6 +59,10 @@ class FollowPackModel: ObservableObject { }) case .eose: continue + case .ndbEose: + continue + case .networkEose: + continue } } } diff --git a/damus/Features/Follows/Models/FollowersModel.swift b/damus/Features/Follows/Models/FollowersModel.swift index e696525f..082025e0 100644 --- a/damus/Features/Follows/Models/FollowersModel.swift +++ b/damus/Features/Follows/Models/FollowersModel.swift @@ -45,6 +45,10 @@ class FollowersModel: ObservableObject { case .eose: guard let txn = NdbTxn(ndb: self.damus_state.ndb) else { return } load_profiles(txn: txn) + case .ndbEose: + continue + case .networkEose: + continue } } } @@ -83,6 +87,10 @@ class FollowersModel: ObservableObject { case .event(let lender): lender.justUseACopy({ self.handle_event(ev: $0) }) case .eose: break + case .ndbEose: + continue + case .networkEose: + continue } } } diff --git a/damus/Features/NIP05/Models/NIP05DomainEventsModel.swift b/damus/Features/NIP05/Models/NIP05DomainEventsModel.swift index 545c0927..137cc787 100644 --- a/damus/Features/NIP05/Models/NIP05DomainEventsModel.swift +++ b/damus/Features/NIP05/Models/NIP05DomainEventsModel.swift @@ -73,6 +73,10 @@ class NIP05DomainEventsModel: ObservableObject { load_profiles(context: "search", load: .from_events(self.events.all_events), damus_state: state, txn: txn) DispatchQueue.main.async { self.loading = false } continue + case .ndbEose: + break + case .networkEose: + break } } } diff --git a/damus/Features/Onboarding/SuggestedUsersViewModel.swift b/damus/Features/Onboarding/SuggestedUsersViewModel.swift index d53f878b..3945765f 100644 --- a/damus/Features/Onboarding/SuggestedUsersViewModel.swift +++ b/damus/Features/Onboarding/SuggestedUsersViewModel.swift @@ -189,30 +189,25 @@ class SuggestedUsersViewModel: ObservableObject { authors: [Constants.ONBOARDING_FOLLOW_PACK_CURATOR_PUBKEY] ) - for await item in self.damus_state.nostrNetwork.reader.subscribe(filters: [filter]) { + for await lender in self.damus_state.nostrNetwork.reader.streamNotesUntilEndOfStoredEvents(filters: [filter]) { // Check for cancellation on each iteration guard !Task.isCancelled else { break } - - switch item { - case .event(let lender): - lender.justUseACopy({ event in - let followPack = FollowPackEvent.parse(from: event) - - guard let id = followPack.uuid else { return } - - let latestPackForThisId: FollowPackEvent - - if let existingPack = packsById[id], existingPack.event.created_at > followPack.event.created_at { - latestPackForThisId = existingPack - } else { - latestPackForThisId = followPack - } - - packsById[id] = latestPackForThisId - }) - case .eose: - break - } + + lender.justUseACopy({ event in + let followPack = FollowPackEvent.parse(from: event) + + guard let id = followPack.uuid else { return } + + let latestPackForThisId: FollowPackEvent + + if let existingPack = packsById[id], existingPack.event.created_at > followPack.event.created_at { + latestPackForThisId = existingPack + } else { + latestPackForThisId = followPack + } + + packsById[id] = latestPackForThisId + }) } } @@ -228,13 +223,8 @@ class SuggestedUsersViewModel: ObservableObject { } let profileFilter = NostrFilter(kinds: [.metadata], authors: allPubkeys) - for await item in damus_state.nostrNetwork.reader.subscribe(filters: [profileFilter]) { - switch item { - case .event(_): - continue // We just need NostrDB to ingest these for them to be available elsewhere, no need to analyze the data - case .eose: - break - } + for await _ in damus_state.nostrNetwork.reader.streamNotesUntilEndOfStoredEvents(filters: [profileFilter]) { + // NO-OP. We just need NostrDB to ingest these for them to be available elsewhere, no need to analyze the data } } } diff --git a/damus/Features/Profile/Models/ProfileModel.swift b/damus/Features/Profile/Models/ProfileModel.swift index a50e252a..eb281c13 100644 --- a/damus/Features/Profile/Models/ProfileModel.swift +++ b/damus/Features/Profile/Models/ProfileModel.swift @@ -81,6 +81,8 @@ class ProfileModel: ObservableObject, Equatable { case .event(let lender): lender.justUseACopy({ handleNostrEvent($0) }) case .eose: break + case .ndbEose: break + case .networkEose: break } } guard let txn = NdbTxn(ndb: damus.ndb) else { return } @@ -97,6 +99,8 @@ class ProfileModel: ObservableObject, Equatable { case .event(let lender): lender.justUseACopy({ handleNostrEvent($0) }) case .eose: break + case .ndbEose: break + case .networkEose: break } } await bumpUpProgress() @@ -138,6 +142,10 @@ class ProfileModel: ObservableObject, Equatable { } case .eose: continue + case .ndbEose: + continue + case .networkEose: + continue } } } @@ -215,6 +223,10 @@ class ProfileModel: ObservableObject, Equatable { } case .eose: break + case .ndbEose: + break + case .networkEose: + break } } } diff --git a/damus/Features/Timeline/Models/HomeModel.swift b/damus/Features/Timeline/Models/HomeModel.swift index 37f64d88..1b3223d9 100644 --- a/damus/Features/Timeline/Models/HomeModel.swift +++ b/damus/Features/Timeline/Models/HomeModel.swift @@ -450,6 +450,9 @@ class HomeModel: ContactsDelegate, ObservableObject { /// Send the initial filters, just our contact list and relay list mostly func send_initial_filters() { Task { + let startTime = CFAbsoluteTimeGetCurrent() + let id = UUID() + Log.info("Initial filter task started with ID %s", for: .homeModel, id.uuidString) let filter = NostrFilter(kinds: [.contacts], limit: 1, authors: [damus_state.pubkey]) for await item in damus_state.nostrNetwork.reader.subscribe(filters: [filter]) { switch item { @@ -459,9 +462,14 @@ class HomeModel: ContactsDelegate, ObservableObject { case .eose: if !done_init { done_init = true + Log.info("Initial filter task %s: Done initialization; Elapsed time: %.2f seconds", for: .homeModel, id.uuidString, CFAbsoluteTimeGetCurrent() - startTime) send_home_filters() } break + case .ndbEose: + break + case .networkEose: + break } } @@ -474,6 +482,8 @@ class HomeModel: ContactsDelegate, ObservableObject { case .event(let lender): await lender.justUseACopy({ await process_event(ev: $0, context: .initialRelayList) }) case .eose: break + case .ndbEose: break + case .networkEose: break } } } @@ -538,6 +548,8 @@ class HomeModel: ContactsDelegate, ObservableObject { case .event(let lender): await lender.justUseACopy({ await process_event(ev: $0, context: .contacts) }) case .eose: continue + case .ndbEose: continue + case .networkEose: continue } } } @@ -550,6 +562,8 @@ class HomeModel: ContactsDelegate, ObservableObject { case .eose: guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } load_profiles(context: "notifications", load: .from_keys(notifications.uniq_pubkeys()), damus_state: damus_state, txn: txn) + case .ndbEose: break + case .networkEose: break } } } @@ -564,6 +578,8 @@ class HomeModel: ContactsDelegate, ObservableObject { var dms = dms.dms.flatMap { $0.events } dms.append(contentsOf: incoming_dms) load_profiles(context: "dms", load: .from_events(dms), damus_state: damus_state, txn: txn) + case .ndbEose: break + case .networkEose: break } } } @@ -580,6 +596,8 @@ class HomeModel: ContactsDelegate, ObservableObject { case .event(let lender): await lender.justUseACopy({ await process_event(ev: $0, context: .nwc) }) case .eose: continue + case .ndbEose: continue + case .networkEose: continue } } } @@ -628,19 +646,43 @@ class HomeModel: ContactsDelegate, ObservableObject { self.homeHandlerTask?.cancel() self.homeHandlerTask = Task { + let startTime = CFAbsoluteTimeGetCurrent() + let id = UUID() + Log.info("Home handler task: Starting home handler task with ID %s", for: .homeModel, id.uuidString) + DispatchQueue.main.async { self.loading = true } - for await item in damus_state.nostrNetwork.reader.subscribe(filters: home_filters) { + for await item in damus_state.nostrNetwork.reader.subscribe(filters: home_filters, id: id) { switch item { case .event(let lender): - await lender.justUseACopy({ await process_event(ev: $0, context: .home) }) + let currentTime = CFAbsoluteTimeGetCurrent() + // Process events in parallel on a separate task, to avoid holding up upcoming signals + // Empirical evidence has shown that in at least one instance this technique saved up to 5 seconds of load time! + Task { await lender.justUseACopy({ await process_event(ev: $0, context: .home) }) } case .eose: + let eoseTime = CFAbsoluteTimeGetCurrent() + Log.info("Home handler task %s: Received general EOSE after %.2f seconds", for: .homeModel, id.uuidString, eoseTime - startTime) + + guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } + load_profiles(context: "home", load: .from_events(events.events), damus_state: damus_state, txn: txn) + + let finishTime = CFAbsoluteTimeGetCurrent() + Log.info("Home handler task %s: Completed initial loading task after %.2f seconds", for: .homeModel, id.uuidString, eoseTime - startTime) + case .ndbEose: + let eoseTime = CFAbsoluteTimeGetCurrent() + Log.info("Home handler task %s: Received NDB EOSE after %.2f seconds", for: .homeModel, id.uuidString, eoseTime - startTime) + guard let txn = NdbTxn(ndb: damus_state.ndb) else { return } DispatchQueue.main.async { self.loading = false } load_profiles(context: "home", load: .from_events(events.events), damus_state: damus_state, txn: txn) + + let finishTime = CFAbsoluteTimeGetCurrent() + Log.info("Home handler task %s: Completed initial NDB loading task after %.2f seconds", for: .homeModel, id.uuidString, eoseTime - startTime) + case .networkEose: + break } } } diff --git a/damus/Features/Wallet/Models/WalletModel.swift b/damus/Features/Wallet/Models/WalletModel.swift index 4c7d9036..f8036485 100644 --- a/damus/Features/Wallet/Models/WalletModel.swift +++ b/damus/Features/Wallet/Models/WalletModel.swift @@ -198,6 +198,10 @@ class WalletModel: ObservableObject { return result case .eose: continue + case .ndbEose: + continue + case .networkEose: + continue } } do { try Task.checkCancellation() } catch { throw .cancelled } diff --git a/damus/Features/Zaps/Models/ZapsModel.swift b/damus/Features/Zaps/Models/ZapsModel.swift index a3e26e67..9eb063c5 100644 --- a/damus/Features/Zaps/Models/ZapsModel.swift +++ b/damus/Features/Zaps/Models/ZapsModel.swift @@ -43,6 +43,10 @@ class ZapsModel: ObservableObject { let events = state.events.lookup_zaps(target: target).map { $0.request.ev } guard let txn = NdbTxn(ndb: state.ndb) else { return } load_profiles(context: "zaps_model", load: .from_events(events), damus_state: state, txn: txn) + case .ndbEose: + break + case .networkEose: + break } } } diff --git a/damus/Shared/Utilities/Log.swift b/damus/Shared/Utilities/Log.swift index 79b9955a..216e7e53 100644 --- a/damus/Shared/Utilities/Log.swift +++ b/damus/Shared/Utilities/Log.swift @@ -24,6 +24,7 @@ enum LogCategory: String { case video_coordination case tips case ndb + case homeModel } /// Damus structured logger diff --git a/damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift b/damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift index de3eb078..ab0b3400 100644 --- a/damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift +++ b/damusTests/NostrNetworkManagerTests/NostrNetworkManagerTests.swift @@ -61,6 +61,10 @@ class NostrNetworkManagerTests: XCTestCase { case .eose: // End of stream, break out of the loop endOfStream.fulfill() + case .ndbEose: + continue + case .networkEose: + continue } } }