import Foundation import Postbox import SwiftSignalKit import TelegramApi import MtProtoKit import SyncCore private final class ManagedPendingPeerNotificationSettingsHelper { var operationDisposables: [PeerId: (PeerNotificationSettings, Disposable)] = [:] func update(entries: [PeerId: PeerNotificationSettings]) -> (disposeOperations: [Disposable], beginOperations: [(PeerId, PeerNotificationSettings, MetaDisposable)]) { var disposeOperations: [Disposable] = [] var beginOperations: [(PeerId, PeerNotificationSettings, MetaDisposable)] = [] var validIds = Set() for (peerId, settings) in entries { validIds.insert(peerId) if let (currentSettings, currentDisposable) = self.operationDisposables[peerId] { if !currentSettings.isEqual(to: settings) { disposeOperations.append(currentDisposable) let disposable = MetaDisposable() beginOperations.append((peerId, settings, disposable)) self.operationDisposables[peerId] = (settings, disposable) } } else { let disposable = MetaDisposable() beginOperations.append((peerId, settings, disposable)) self.operationDisposables[peerId] = (settings, disposable) } } var removeIds: [PeerId] = [] for (id, settingsAndDisposable) in self.operationDisposables { if !validIds.contains(id) { removeIds.append(id) disposeOperations.append(settingsAndDisposable.1) } } for id in removeIds { self.operationDisposables.removeValue(forKey: id) } return (disposeOperations, beginOperations) } func reset() -> [Disposable] { let disposables = Array(self.operationDisposables.values).map { $0.1 } self.operationDisposables.removeAll() return disposables } } func managedPendingPeerNotificationSettings(postbox: Postbox, network: Network) -> Signal { return Signal { _ in let helper = Atomic(value: ManagedPendingPeerNotificationSettingsHelper()) let disposable = postbox.combinedView(keys: [.pendingPeerNotificationSettings]).start(next: { view in var entries: [PeerId: PeerNotificationSettings] = [:] if let v = view.views[.pendingPeerNotificationSettings] as? PendingPeerNotificationSettingsView { entries = v.entries } let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PeerId, PeerNotificationSettings, MetaDisposable)]) in return helper.update(entries: entries) } for disposable in disposeOperations { disposable.dispose() } for (peerId, settings, disposable) in beginOperations { let signal = pushPeerNotificationSettings(postbox: postbox, network: network, peerId: peerId, settings: settings) disposable.set(signal.start()) } }) return ActionDisposable { let disposables = helper.with { helper -> [Disposable] in return helper.reset() } for disposable in disposables { disposable.dispose() } disposable.dispose() } } } private func pushPeerNotificationSettings(postbox: Postbox, network: Network, peerId: PeerId, settings: PeerNotificationSettings) -> Signal { return postbox.transaction { transaction -> Signal in if let peer = transaction.getPeer(peerId) { var notificationPeerId = peerId if let associatedPeerId = peer.associatedPeerId { notificationPeerId = associatedPeerId } if let notificationPeer = transaction.getPeer(notificationPeerId), let inputPeer = apiInputPeer(notificationPeer), let settings = settings as? TelegramPeerNotificationSettings { let showPreviews: Api.Bool? switch settings.displayPreviews { case .default: showPreviews = nil case .show: showPreviews = .boolTrue case .hide: showPreviews = .boolFalse } let muteUntil: Int32? switch settings.muteState { case let .muted(until): muteUntil = until case .unmuted: muteUntil = 0 case .default: muteUntil = nil } let sound: String? = settings.messageSound.apiSound var flags: Int32 = 0 if showPreviews != nil { flags |= (1 << 0) } if muteUntil != nil { flags |= (1 << 2) } if sound != nil { flags |= (1 << 3) } let inputSettings = Api.InputPeerNotifySettings.inputPeerNotifySettings(flags: flags, showPreviews: showPreviews, silent: nil, muteUntil: muteUntil, sound: sound) return network.request(Api.functions.account.updateNotifySettings(peer: .inputNotifyPeer(peer: inputPeer), settings: inputSettings)) |> retryRequest |> mapToSignal { result -> Signal in return postbox.transaction { transaction -> Void in transaction.updateCurrentPeerNotificationSettings([notificationPeerId: settings]) if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) { transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil) } } } } else { if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) { transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil) } return .complete() } } else { if let pending = transaction.getPendingPeerNotificationSettings(peerId), pending.isEqual(to: settings) { transaction.updatePendingPeerNotificationSettings(peerId: peerId, settings: nil) } return .complete() } } |> switchToLatest }