Memory management (#503)

* Correctly tear down the user session on signing out

* Fix session verification <-> user session retain cycle, visible range debouncer leak

* Manually clean up coordinators retained within SwiftUI's NavigationStacks

* Slightly refactor the timeline content menu builder and prevent it from retaining the view model. Cleanup now unnecessarily optional RoomScreenCoordinator instance vars

* Move coordinator dismissal logic to the navigation modules
This commit is contained in:
Stefan Ceriu
2023-01-31 11:51:56 +02:00
committed by GitHub
parent 70cff446cf
commit d51a9b3a2b
13 changed files with 152 additions and 147 deletions
@@ -17,11 +17,17 @@
import SwiftUI
struct TimelineItemContextMenuActions {
static var empty: TimelineItemContextMenuActions { .init(actions: [], debugActions: []) }
let actions: [TimelineItemContextMenuAction]
let debugActions: [TimelineItemContextMenuAction]
var isEmpty: Bool { actions.isEmpty && debugActions.isEmpty }
init?(actions: [TimelineItemContextMenuAction], debugActions: [TimelineItemContextMenuAction]) {
if actions.isEmpty, debugActions.isEmpty {
return nil
}
self.actions = actions
self.debugActions = debugActions
}
}
enum TimelineItemContextMenuAction: Identifiable, Hashable {
@@ -52,16 +58,11 @@ public struct TimelineItemContextMenu: View {
let callback: (TimelineItemContextMenuAction) -> Void
public var body: some View {
if contextMenuActions.isEmpty {
// When there are no actions make sure then menu isn't shown.
EmptyView()
} else {
viewsForActions(contextMenuActions.actions)
Menu {
viewsForActions(contextMenuActions.debugActions)
} label: {
Label("Developer", systemImage: "hammer")
}
viewsForActions(contextMenuActions.actions)
Menu {
viewsForActions(contextMenuActions.debugActions)
} label: {
Label("Developer", systemImage: "hammer")
}
}
@@ -72,7 +72,7 @@ class TimelineTableViewController: UIViewController {
}
}
var contextMenuBuilder: (@MainActor (_ itemId: String) -> TimelineItemContextMenu)?
var contextMenuActionProvider: (@MainActor (_ itemId: String) -> TimelineItemContextMenuActions?)?
@Binding private var scrollToBottomButtonVisible: Bool
@@ -189,7 +189,7 @@ class TimelineTableViewController: UIViewController {
// A local reference to avoid capturing self in the cell configuration.
let coordinator = self.coordinator
let opacity = self.opacity(for: timelineItem)
let contextMenuBuilder = self.contextMenuBuilder
let contextMenuActionProvider = self.contextMenuActionProvider
cell.item = timelineItem
cell.contentConfiguration = UIHostingConfiguration {
@@ -198,7 +198,11 @@ class TimelineTableViewController: UIViewController {
.frame(maxWidth: .infinity, alignment: .leading)
.opacity(opacity)
.contextMenu {
contextMenuBuilder?(timelineItem.id)
contextMenuActionProvider?(timelineItem.id).map { actions in
TimelineItemContextMenu(contextMenuActions: actions) { action in
coordinator.send(viewAction: .contextMenuAction(itemID: timelineItem.id, action: action))
}
}
}
.onAppear {
coordinator.send(viewAction: .itemAppeared(id: timelineItem.id))
@@ -70,7 +70,7 @@ struct TimelineView: UIViewControllerRepresentable {
}
// Doesn't have an equatable conformance :(
tableViewController.contextMenuBuilder = context.viewState.contextMenuBuilder
tableViewController.contextMenuActionProvider = context.viewState.contextMenuActionProvider
}
func send(viewAction: RoomScreenViewAction) {