Offload note filtering computations from the view body render function
This attempts to improve the performance of InnerTimelineView by performing event filtering computations on "EventHolder.insert" instead of on each view body re-render, to improve SwiftUI performance. Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -9,14 +9,18 @@ import SwiftUI
|
|||||||
|
|
||||||
|
|
||||||
struct InnerTimelineView: View {
|
struct InnerTimelineView: View {
|
||||||
@ObservedObject var events: EventHolder
|
var events: EventHolder
|
||||||
|
@ObservedObject var filteredEvents: EventHolder.FilteredHolder
|
||||||
|
var filteredEventHolderId: UUID
|
||||||
let state: DamusState
|
let state: DamusState
|
||||||
let filter: (NostrEvent) -> Bool
|
|
||||||
|
|
||||||
init(events: EventHolder, damus: DamusState, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true) {
|
init(events: EventHolder, damus: DamusState, filter: @escaping (NostrEvent) -> Bool, apply_mute_rules: Bool = true) {
|
||||||
self.events = events
|
self.events = events
|
||||||
self.state = damus
|
self.state = damus
|
||||||
self.filter = apply_mute_rules ? { filter($0) && !damus.mutelist_manager.is_event_muted($0) } : filter
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
var event_options: EventViewOptions {
|
var event_options: EventViewOptions {
|
||||||
@@ -29,12 +33,11 @@ struct InnerTimelineView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
let events = self.events.events
|
let events = self.filteredEvents.events
|
||||||
if events.isEmpty {
|
if events.isEmpty {
|
||||||
EmptyTimelineView()
|
EmptyTimelineView()
|
||||||
} else {
|
} else {
|
||||||
let evs = events.filter(filter)
|
let indexed = Array(zip(events, 0...))
|
||||||
let indexed = Array(zip(evs, 0...))
|
|
||||||
ForEach(indexed, id: \.0.id) { tup in
|
ForEach(indexed, id: \.0.id) { tup in
|
||||||
let ev = tup.0
|
let ev = tup.0
|
||||||
let ind = tup.1
|
let ind = tup.1
|
||||||
@@ -62,6 +65,9 @@ struct InnerTimelineView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onDisappear {
|
||||||
|
self.events.removeFilteredHolder(id: self.filteredEventHolderId)
|
||||||
|
}
|
||||||
//.padding(.horizontal)
|
//.padding(.horizontal)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import Foundation
|
|||||||
class EventHolder: ObservableObject, ScrollQueue {
|
class EventHolder: ObservableObject, ScrollQueue {
|
||||||
private var has_event = Set<NoteId>()
|
private var has_event = Set<NoteId>()
|
||||||
@Published var events: [NostrEvent]
|
@Published var events: [NostrEvent]
|
||||||
|
var filteredHolders: [UUID: FilteredHolder] = [:]
|
||||||
var incoming: [NostrEvent]
|
var incoming: [NostrEvent]
|
||||||
private(set) var should_queue = false
|
private(set) var should_queue = false
|
||||||
var on_queue: ((NostrEvent) -> Void)?
|
var on_queue: ((NostrEvent) -> Void)?
|
||||||
@@ -58,6 +59,9 @@ class EventHolder: ObservableObject, ScrollQueue {
|
|||||||
if insert_uniq_sorted_event_created(events: &self.events, new_ev: ev) {
|
if insert_uniq_sorted_event_created(events: &self.events, new_ev: ev) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
for (id, filteredView) in self.filteredHolders {
|
||||||
|
filteredView.insert(event: ev)
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -85,6 +89,9 @@ class EventHolder: ObservableObject, ScrollQueue {
|
|||||||
if insert_uniq_sorted_event_created(events: &events, new_ev: event) {
|
if insert_uniq_sorted_event_created(events: &events, new_ev: event) {
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
for (id, filteredHolder) in self.filteredHolders {
|
||||||
|
filteredHolder.insert(event: event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
@@ -100,5 +107,46 @@ class EventHolder: ObservableObject, ScrollQueue {
|
|||||||
func reset() {
|
func reset() {
|
||||||
self.incoming = []
|
self.incoming = []
|
||||||
self.events = []
|
self.events = []
|
||||||
|
for (id, filteredHolder) in filteredHolders {
|
||||||
|
filteredHolder.update(events: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func add(filteredHolder: FilteredHolder) -> UUID {
|
||||||
|
let id = UUID()
|
||||||
|
self.filteredHolders[id] = filteredHolder
|
||||||
|
filteredHolder.update(events: self.events)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func removeFilteredHolder(id: UUID) {
|
||||||
|
self.filteredHolders[id] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilteredHolder: ObservableObject {
|
||||||
|
@Published private(set) var events: [NostrEvent]
|
||||||
|
let filter: (NostrEvent) -> Bool
|
||||||
|
|
||||||
|
init(filter: @escaping (NostrEvent) -> Bool) {
|
||||||
|
self.events = []
|
||||||
|
self.filter = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
func update(events: [NostrEvent]) {
|
||||||
|
self.events = events.filter(self.filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func insert(event: NostrEvent) {
|
||||||
|
guard self.filter(event) else { return }
|
||||||
|
var changed = false
|
||||||
|
if insert_uniq_sorted_event_created(events: &events, new_ev: event) {
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
self.objectWillChange.send()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user