Fix app freeze
This commit fixes an issue where the app would occasionally freeze. The filtered holders were being initialized and registered directly from a SwiftUI initializer, which would sometimes cause hundreds of instances to be initialized and registered and never removed by `onDisappear`. The issue was fixed by initializing such objects with `StateObject`, which brings it a more stable identity that lives as long as the SwiftUI view it is in, and by placing the init/deinit registration/clean-up logic in the filtered holder object itself, better matching the lifecycle and preventing resource leakage. Changelog-Fixed: Fixed an issue that would occasionally cause the app to freeze Closes: https://github.com/damus-io/damus/issues/3383 Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -10,17 +10,14 @@ import SwiftUI
|
||||
|
||||
struct InnerTimelineView: View {
|
||||
var events: EventHolder
|
||||
@ObservedObject var filteredEvents: EventHolder.FilteredHolder
|
||||
var filteredEventHolderId: UUID
|
||||
@StateObject var filteredEvents: EventHolder.FilteredHolder
|
||||
let state: DamusState
|
||||
|
||||
init(events: EventHolder, damus: DamusState, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true) {
|
||||
self.events = events
|
||||
self.state = damus
|
||||
let filter = apply_mute_rules ? { filter($0) && !damus.mutelist_manager.is_event_muted($0) } : filter
|
||||
let filteredEvents = EventHolder.FilteredHolder(filter: filter)
|
||||
self.filteredEvents = filteredEvents
|
||||
self.filteredEventHolderId = events.add(filteredHolder: filteredEvents)
|
||||
_filteredEvents = StateObject.init(wrappedValue: EventHolder.FilteredHolder(filter: filter, parent: events))
|
||||
}
|
||||
|
||||
var event_options: EventViewOptions {
|
||||
@@ -65,11 +62,6 @@ struct InnerTimelineView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
self.events.removeFilteredHolder(id: self.filteredEventHolderId)
|
||||
}
|
||||
//.padding(.horizontal)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,16 +56,20 @@ class EventHolder: ObservableObject, ScrollQueue {
|
||||
|
||||
has_event.insert(ev.id)
|
||||
|
||||
var changed = false
|
||||
|
||||
if insert_uniq_sorted_event_created(events: &self.events, new_ev: ev) {
|
||||
return true
|
||||
changed = true
|
||||
}
|
||||
for (id, filteredView) in self.filteredHolders {
|
||||
|
||||
for (_, filteredView) in self.filteredHolders {
|
||||
filteredView.insert(event: ev)
|
||||
}
|
||||
|
||||
return false
|
||||
return changed
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func insert_queued(_ ev: NostrEvent) -> Bool {
|
||||
if has_event.contains(ev.id) {
|
||||
return false
|
||||
@@ -79,6 +83,7 @@ class EventHolder: ObservableObject, ScrollQueue {
|
||||
return true
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func flush() {
|
||||
guard !incoming.isEmpty else {
|
||||
return
|
||||
@@ -89,7 +94,7 @@ class EventHolder: ObservableObject, ScrollQueue {
|
||||
if insert_uniq_sorted_event_created(events: &events, new_ev: event) {
|
||||
changed = true
|
||||
}
|
||||
for (id, filteredHolder) in self.filteredHolders {
|
||||
for (_, filteredHolder) in self.filteredHolders {
|
||||
filteredHolder.insert(event: event)
|
||||
}
|
||||
}
|
||||
@@ -107,7 +112,7 @@ class EventHolder: ObservableObject, ScrollQueue {
|
||||
func reset() {
|
||||
self.incoming = []
|
||||
self.events = []
|
||||
for (id, filteredHolder) in filteredHolders {
|
||||
for (_, filteredHolder) in filteredHolders {
|
||||
filteredHolder.update(events: [])
|
||||
}
|
||||
}
|
||||
@@ -125,13 +130,26 @@ class EventHolder: ObservableObject, ScrollQueue {
|
||||
self.filteredHolders[id] = nil
|
||||
}
|
||||
|
||||
@MainActor
|
||||
class FilteredHolder: ObservableObject {
|
||||
@Published private(set) var events: [NostrEvent]
|
||||
let filter: (NostrEvent) -> Bool
|
||||
private var id: UUID?
|
||||
private weak var parent: EventHolder?
|
||||
|
||||
init(filter: @escaping (NostrEvent) -> Bool) {
|
||||
init(filter: @escaping (NostrEvent) -> Bool, parent: EventHolder) {
|
||||
self.events = []
|
||||
self.filter = filter
|
||||
self.parent = parent
|
||||
self.id = parent.add(filteredHolder: self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
// Capture for async cleanup
|
||||
guard let id = id, let parent = parent else { return }
|
||||
Task { @MainActor in
|
||||
parent.removeFilteredHolder(id: id)
|
||||
}
|
||||
}
|
||||
|
||||
func update(events: [NostrEvent]) {
|
||||
|
||||
Reference in New Issue
Block a user