Files
element-x-ios/ElementX/Sources/Services/Presence/PresenceService.swift
T
Doug ac29354b08 Move any visible account settings into AccountSettings. (#6308)
* Remove the now unused setting to disable in-app notifications.

* Move visible account settings into AccountSettings.

Have ignored enableNotifications for now as that will require reworking the NotificationManager.

* Allow different sounds to be selected for each account.

* Move defaultNotificationTone into AccountSettings.
2026-10-13 15:21:13 +01:00

147 lines
4.2 KiB
Swift

//
// Copyright 2026 Element Creations 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 Foundation
import UIKit
final class PresenceService {
private let clientProxy: ClientProxyProtocol
private let userSettings: UserSettings
private let notificationCenter: NotificationCenter
private var isForegroundActive: Bool
private var lastSentPresence: ClientProxyPresence?
private var pendingPresence: ClientProxyPresence?
private var sendTask: Task<Void, Never>?
private var cancellables = Set<AnyCancellable>()
init(clientProxy: ClientProxyProtocol,
userSettings: UserSettings,
notificationCenter: NotificationCenter = .default,
initialApplicationState: UIApplication.State = UIApplication.shared.applicationState) {
self.clientProxy = clientProxy
self.userSettings = userSettings
self.notificationCenter = notificationCenter
isForegroundActive = initialApplicationState == .active
observeApplicationState()
observeSharePresence()
reportCurrentState()
}
isolated deinit {
sendTask?.cancel()
cancellables.forEach { $0.cancel() }
}
private var desiredPresence: ClientProxyPresence {
switch (isForegroundActive, userSettings.account.sharePresence) {
case (true, true):
.online
case (true, false):
.offline
case (false, true):
.unavailable
case (false, false):
.offline
}
}
private func observeApplicationState() {
notificationCenter.publisher(for: UIApplication.willResignActiveNotification)
.sink { [weak self] _ in
self?.apply(applicationState: .inactive)
}
.store(in: &cancellables)
notificationCenter.publisher(for: UIApplication.didBecomeActiveNotification)
.sink { [weak self] _ in
self?.apply(applicationState: .active)
}
.store(in: &cancellables)
}
private func observeSharePresence() {
userSettings.account.sharePresencePublisher
.removeDuplicates()
.sink { [weak self] _ in
self?.reportCurrentState()
}
.store(in: &cancellables)
}
private func apply(applicationState: UIApplication.State) {
isForegroundActive = applicationState == .active
reportCurrentState()
}
private func reportCurrentState() {
let presence = desiredPresence
guard sendTask != nil || presence != lastSentPresence else {
return
}
guard pendingPresence != presence else {
return
}
pendingPresence = presence
startSendTaskIfNeeded()
}
private func startSendTaskIfNeeded() {
guard sendTask == nil else {
return
}
sendTask = Task { [weak self, clientProxy] in
while !Task.isCancelled {
guard let presence = self?.nextPendingPresence() else {
break
}
_ = await clientProxy.configurePresence(presence, sendImmediately: true)
guard !Task.isCancelled else {
break
}
self?.finishSending(presence)
}
self?.finishSendTask()
}
}
private func nextPendingPresence() -> ClientProxyPresence? {
guard let pendingPresence else {
return nil
}
self.pendingPresence = nil
return pendingPresence
}
private func finishSending(_ presence: ClientProxyPresence) {
lastSentPresence = presence
if pendingPresence == presence {
pendingPresence = nil
}
}
private func finishSendTask() {
sendTask = nil
if pendingPresence != nil {
startSendTaskIfNeeded()
}
}
}