Beam/ElementX/Sources/Other/UserIndicator/UserIndicatorController.swift

96 lines
3.1 KiB
Swift
Raw Normal View History

//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
//
import Combine
import SwiftUI
class UserIndicatorController: ObservableObject, UserIndicatorControllerProtocol {
private var timerCancellable: AnyCancellable?
private var displayTimes = [String: Date]()
private var delayedIndicators = Set<String>()
var nonPersistentDisplayDuration = 2.5
var minimumDisplayDuration = 0.5
@Published private(set) var activeIndicator: UserIndicator?
private(set) var indicatorQueue = [UserIndicator]() {
didSet {
activeIndicator = indicatorQueue.last
if let activeIndicator, !activeIndicator.persistent {
timerCancellable?.cancel()
timerCancellable = Task { [weak self, nonPersistentDisplayDuration] in
try await Task.sleep(for: .seconds(nonPersistentDisplayDuration))
self?.retractIndicatorWithId(activeIndicator.id)
}.asCancellable()
}
}
}
@Published var alertInfo: AlertInfo<UUID>?
var window: UIWindow? {
didSet {
let hostingController = UIHostingController(rootView: UserIndicatorPresenter(userIndicatorController: self).statusBarHidden(ProcessInfo.isRunningUITests))
hostingController.view.backgroundColor = .clear
window?.rootViewController = hostingController
}
}
func submitIndicator(_ indicator: UserIndicator, delay: Duration?) {
if let index = indicatorQueue.firstIndex(where: { $0.id == indicator.id }) {
indicatorQueue[index] = indicator
displayTimes[indicator.id] = .now
} else {
if let delay {
delayedIndicators.insert(indicator.id)
Task {
try await Task.sleep(for: .seconds(delay.seconds))
guard delayedIndicators.contains(indicator.id) else {
return
}
enqueue(indicator: indicator)
}
} else {
enqueue(indicator: indicator)
}
}
}
func retractAllIndicators() {
for indicator in indicatorQueue {
retractIndicatorWithId(indicator.id)
}
}
func retractIndicatorWithId(_ id: String) {
delayedIndicators.remove(id)
guard let displayTime = displayTimes[id], abs(displayTime.timeIntervalSinceNow) <= minimumDisplayDuration else {
indicatorQueue.removeAll { $0.id == id }
return
}
Task {
try? await Task.sleep(for: .seconds(minimumDisplayDuration))
indicatorQueue.removeAll { $0.id == id }
displayTimes[id] = nil
}
}
Fixes #317 - Adopt a split layout for iPad and Mac apps Rename navigation components: SplitScreenCoordinator -> NavigationSplitCoordinator, StackScreenCoordinator -> NavigationStackCoordinator and SingleScreenCoordinator -> NavigationRootCoordinator [0c161039] Tweak navigation logging [826c19cf] Move the navigation dismissal callbacks to the NavigationModule, add SingleScreenCoordinator tests [b8830d9c] Add tests [252ad119] Merge the StackScreenCoordinator and SplitScreenCoordinators into a single file and stop publicly exposing their internal workings. Add more documentation. [37671699] Cleanup navigation logging [51406184] Use the parent SplitScreenCoordinator to present embedded StackScreenCoordinator sheets [b94b04c9] Retract the room "syncing" indicator when dismissing a room [1467b0ac] Correctly move to the no room selected state when popping in compact layouts [10bf2ad8] Allow nilling root coordinators, replace present/dismiss sheet with setSheetCoordinator(?) [33716784] Add single screen coordinator fade transition animation [3cbe65e7] Prevent the timeline table view from being reused between different rooms [9c94c50b] Move files around [c10b6bc5] Adapt the user session state machine to the split layout [7115a319] Fix unit and UI tests [1ece59e8] Fix login flows [6884dc3b] Use modules everywhere the underlying object is a NavigationModule [ab08d44c] Rename navigation components to: SingleScreenCoordinator, SplitScreenCoordinator and StackScreenCoordinator [ada2be57] Add SplitNavigationController * Remove the navigationRootCoordinator from the UserSessionFlowCoordinator
2022-12-12 12:31:27 +02:00
// MARK: - Private
private func enqueue(indicator: UserIndicator) {
retractIndicatorWithId(indicator.id)
indicatorQueue.append(indicator)
displayTimes[indicator.id] = .now
}
}