mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
311 lines
13 KiB
Swift
311 lines
13 KiB
Swift
import Foundation
|
|
|
|
private struct PeerNotificationSettingsTableEntry: Equatable {
|
|
let current: PeerNotificationSettings?
|
|
let pending: PeerNotificationSettings?
|
|
|
|
static func ==(lhs: PeerNotificationSettingsTableEntry, rhs: PeerNotificationSettingsTableEntry) -> Bool {
|
|
if let lhsCurrent = lhs.current, let rhsCurrent = rhs.current {
|
|
if !lhsCurrent.isEqual(to: rhsCurrent) {
|
|
return false
|
|
}
|
|
} else if (lhs.current != nil) != (rhs.current != nil) {
|
|
return false
|
|
}
|
|
if let lhsPending = lhs.pending, let rhsPending = rhs.pending {
|
|
if !lhsPending.isEqual(to: rhsPending) {
|
|
return false
|
|
}
|
|
} else if (lhs.pending != nil) != (rhs.pending != nil) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
var effective: PeerNotificationSettings? {
|
|
if let pending = self.pending {
|
|
return pending
|
|
}
|
|
return self.current
|
|
}
|
|
|
|
func withUpdatedCurrent(_ current: PeerNotificationSettings?) -> PeerNotificationSettingsTableEntry {
|
|
return PeerNotificationSettingsTableEntry(current: current, pending: self.pending)
|
|
}
|
|
|
|
func withUpdatedPending(_ pending: PeerNotificationSettings?) -> PeerNotificationSettingsTableEntry {
|
|
return PeerNotificationSettingsTableEntry(current: self.current, pending: pending)
|
|
}
|
|
}
|
|
|
|
private struct PeerNotificationSettingsTableEntryFlags: OptionSet {
|
|
var rawValue: Int32
|
|
|
|
init(rawValue: Int32) {
|
|
self.rawValue = rawValue
|
|
}
|
|
|
|
static let hasCurrent = PeerNotificationSettingsTableEntryFlags(rawValue: 1 << 0)
|
|
static let hasPending = PeerNotificationSettingsTableEntryFlags(rawValue: 1 << 1)
|
|
}
|
|
|
|
final class PeerNotificationSettingsTable: Table {
|
|
static func tableSpec(_ id: Int32) -> ValueBoxTable {
|
|
return ValueBoxTable(id: id, keyType: .int64, compactValuesOnCreation: true)
|
|
}
|
|
|
|
private let pendingIndexTable: PendingPeerNotificationSettingsIndexTable
|
|
private let behaviorTable: PeerNotificationSettingsBehaviorTable
|
|
|
|
private let sharedEncoder = PostboxEncoder()
|
|
private let sharedKey = ValueBoxKey(length: 8)
|
|
|
|
private var cachedSettings: [PeerId: PeerNotificationSettingsTableEntry] = [:]
|
|
private var updatedInitialSettings: [PeerId: PeerNotificationSettingsTableEntry] = [:]
|
|
|
|
init(valueBox: ValueBox, table: ValueBoxTable, useCaches: Bool, pendingIndexTable: PendingPeerNotificationSettingsIndexTable, behaviorTable: PeerNotificationSettingsBehaviorTable) {
|
|
self.pendingIndexTable = pendingIndexTable
|
|
self.behaviorTable = behaviorTable
|
|
|
|
super.init(valueBox: valueBox, table: table, useCaches: useCaches)
|
|
}
|
|
|
|
private func key(_ id: PeerId) -> ValueBoxKey {
|
|
self.sharedKey.setInt64(0, value: id.toInt64())
|
|
return self.sharedKey
|
|
}
|
|
|
|
private func getEntry(_ id: PeerId, _ value: ReadBuffer? = nil) -> PeerNotificationSettingsTableEntry {
|
|
if let entry = self.cachedSettings[id] {
|
|
return entry
|
|
} else if let value = value ?? self.valueBox.get(self.table, key: self.key(id)) {
|
|
var flagsValue: Int32 = 0
|
|
value.read(&flagsValue, offset: 0, length: 4)
|
|
let flags = PeerNotificationSettingsTableEntryFlags(rawValue: flagsValue)
|
|
|
|
var current: PeerNotificationSettings?
|
|
if flags.contains(.hasCurrent) {
|
|
var length: Int32 = 0
|
|
value.read(&length, offset: 0, length: 4)
|
|
let object = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(length), length: Int(length), freeWhenDone: false)).decodeRootObject() as? PeerNotificationSettings
|
|
assert(object != nil)
|
|
current = object
|
|
value.skip(Int(length))
|
|
}
|
|
|
|
var pending: PeerNotificationSettings?
|
|
if flags.contains(.hasPending) {
|
|
var length: Int32 = 0
|
|
value.read(&length, offset: 0, length: 4)
|
|
let object = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(length), length: Int(length), freeWhenDone: false)).decodeRootObject() as? PeerNotificationSettings
|
|
assert(object != nil)
|
|
pending = object
|
|
value.skip(Int(length))
|
|
}
|
|
let entry = PeerNotificationSettingsTableEntry(current: current, pending: pending)
|
|
self.cachedSettings[id] = entry
|
|
return entry
|
|
} else {
|
|
let entry = PeerNotificationSettingsTableEntry(current: nil, pending: nil)
|
|
self.cachedSettings[id] = entry
|
|
return entry
|
|
}
|
|
}
|
|
|
|
func setCurrent(id: PeerId, settings: PeerNotificationSettings?, updatedTimestamps: inout [PeerId: PeerNotificationSettingsBehaviorTimestamp]) -> PeerNotificationSettings? {
|
|
let currentEntry = self.getEntry(id)
|
|
var updated = false
|
|
if let current = currentEntry.current, let settings = settings {
|
|
updated = !current.isEqual(to: settings)
|
|
} else if (currentEntry.current != nil) != (settings != nil) {
|
|
updated = true
|
|
}
|
|
if updated {
|
|
var behaviorTimestamp: Int32?
|
|
if let settings = settings {
|
|
switch settings.behavior {
|
|
case .none:
|
|
break
|
|
case let .reset(atTimestamp, _):
|
|
behaviorTimestamp = atTimestamp
|
|
}
|
|
}
|
|
self.behaviorTable.set(peerId: id, timestamp: behaviorTimestamp, updatedTimestamps: &updatedTimestamps)
|
|
if self.updatedInitialSettings[id] == nil {
|
|
self.updatedInitialSettings[id] = currentEntry
|
|
}
|
|
let updatedEntry = currentEntry.withUpdatedCurrent(settings)
|
|
self.cachedSettings[id] = updatedEntry
|
|
return updatedEntry.effective
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func setPending(id: PeerId, settings: PeerNotificationSettings?, updatedSettings: inout Set<PeerId>) -> PeerNotificationSettings? {
|
|
let currentEntry = self.getEntry(id)
|
|
var updated = false
|
|
if let pending = currentEntry.pending, let settings = settings {
|
|
updated = !pending.isEqual(to: settings)
|
|
} else if (currentEntry.pending != nil) != (settings != nil) {
|
|
updated = true
|
|
}
|
|
if updated {
|
|
if self.updatedInitialSettings[id] == nil {
|
|
self.updatedInitialSettings[id] = currentEntry
|
|
}
|
|
updatedSettings.insert(id)
|
|
let updatedEntry = currentEntry.withUpdatedPending(settings)
|
|
self.cachedSettings[id] = updatedEntry
|
|
self.pendingIndexTable.set(peerId: id, pending: updatedEntry.pending != nil)
|
|
return updatedEntry.effective
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func getCurrent(_ id: PeerId) -> PeerNotificationSettings? {
|
|
return self.getEntry(id).current
|
|
}
|
|
|
|
func getEffective(_ id: PeerId) -> PeerNotificationSettings? {
|
|
return self.getEntry(id).effective
|
|
}
|
|
|
|
func getPending(_ id: PeerId) -> PeerNotificationSettings? {
|
|
return self.getEntry(id).pending
|
|
}
|
|
|
|
func getAll() -> [PeerId: PeerNotificationSettings] {
|
|
var allSettings: [PeerId: PeerNotificationSettings] = [:]
|
|
valueBox.scanInt64(self.table, values: { key, value in
|
|
let peerId = PeerId(key)
|
|
let entry = self.getEntry(peerId, value)
|
|
if let settings = entry.effective {
|
|
allSettings[peerId] = settings
|
|
}
|
|
return true
|
|
})
|
|
|
|
return allSettings
|
|
}
|
|
|
|
override func clearMemoryCache() {
|
|
self.cachedSettings.removeAll()
|
|
self.updatedInitialSettings.removeAll()
|
|
}
|
|
|
|
func transactionParticipationInTotalUnreadCountUpdates(postbox: PostboxImpl, transaction: Transaction) -> (added: Set<PeerId>, removed: Set<PeerId>) {
|
|
var added = Set<PeerId>()
|
|
var removed = Set<PeerId>()
|
|
|
|
var globalNotificationSettings: PostboxGlobalNotificationSettings?
|
|
|
|
for (peerId, initialSettings) in self.updatedInitialSettings {
|
|
guard let peer = postbox.peerTable.get(peerId) else {
|
|
continue
|
|
}
|
|
|
|
let globalNotificationSettingsValue: PostboxGlobalNotificationSettings
|
|
if let current = globalNotificationSettings {
|
|
globalNotificationSettingsValue = current
|
|
} else {
|
|
globalNotificationSettingsValue = postbox.getGlobalNotificationSettings(transaction: transaction)
|
|
globalNotificationSettings = globalNotificationSettingsValue
|
|
}
|
|
|
|
let wasParticipating = !resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: initialSettings.effective)
|
|
let isParticipating = !resolvedIsRemovedFromTotalUnreadCount(globalSettings: globalNotificationSettingsValue, peer: peer, peerSettings: self.cachedSettings[peerId]?.effective)
|
|
|
|
if wasParticipating != isParticipating {
|
|
if isParticipating {
|
|
added.insert(peerId)
|
|
} else {
|
|
removed.insert(peerId)
|
|
}
|
|
}
|
|
}
|
|
|
|
return (added, removed)
|
|
}
|
|
|
|
func resetAll(to settings: PeerNotificationSettings, updatedSettings: inout Set<PeerId>, updatedTimestamps: inout [PeerId: PeerNotificationSettingsBehaviorTimestamp]) -> [PeerId: PeerNotificationSettings?] {
|
|
let lowerBound = ValueBoxKey(length: 8)
|
|
lowerBound.setInt64(0, value: 0)
|
|
let upperBound = ValueBoxKey(length: 8)
|
|
upperBound.setInt64(0, value: Int64.max)
|
|
var peerIds: [PeerId] = []
|
|
self.valueBox.range(self.table, start: lowerBound, end: upperBound, keys: { key in
|
|
peerIds.append(PeerId(key.getInt64(0)))
|
|
return true
|
|
}, limit: 0)
|
|
|
|
var updatedPeers: [PeerId: PeerNotificationSettings?] = [:]
|
|
for peerId in peerIds {
|
|
let entry = self.getEntry(peerId)
|
|
if let current = entry.current, !current.isEqual(to: settings) || entry.pending != nil {
|
|
let _ = self.setCurrent(id: peerId, settings: settings, updatedTimestamps: &updatedTimestamps)
|
|
let _ = self.setPending(id: peerId, settings: nil, updatedSettings: &updatedSettings)
|
|
updatedPeers[peerId] = entry.effective
|
|
}
|
|
}
|
|
|
|
return updatedPeers
|
|
}
|
|
|
|
override func beforeCommit() {
|
|
if !self.updatedInitialSettings.isEmpty {
|
|
let buffer = WriteBuffer()
|
|
let encoder = PostboxEncoder()
|
|
for (peerId, _) in self.updatedInitialSettings {
|
|
if let entry = self.cachedSettings[peerId] {
|
|
buffer.reset()
|
|
|
|
var flags = PeerNotificationSettingsTableEntryFlags()
|
|
if entry.current != nil {
|
|
flags.insert(.hasCurrent)
|
|
}
|
|
if entry.pending != nil {
|
|
flags.insert(.hasPending)
|
|
}
|
|
|
|
var flagsValue: Int32 = flags.rawValue
|
|
buffer.write(&flagsValue, offset: 0, length: 4)
|
|
|
|
if let current = entry.current {
|
|
encoder.reset()
|
|
encoder.encodeRootObject(current)
|
|
let object = encoder.readBufferNoCopy()
|
|
withExtendedLifetime(object, {
|
|
var length: Int32 = Int32(object.length)
|
|
buffer.write(&length, offset: 0, length: 4)
|
|
buffer.write(object.memory, offset: 0, length: object.length)
|
|
})
|
|
}
|
|
|
|
if let pending = entry.pending {
|
|
encoder.reset()
|
|
encoder.encodeRootObject(pending)
|
|
let object = encoder.readBufferNoCopy()
|
|
withExtendedLifetime(object, {
|
|
var length: Int32 = Int32(object.length)
|
|
buffer.write(&length, offset: 0, length: 4)
|
|
buffer.write(object.memory, offset: 0, length: object.length)
|
|
})
|
|
}
|
|
|
|
self.valueBox.set(self.table, key: self.key(peerId), value: buffer.readBufferNoCopy())
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
}
|
|
|
|
self.updatedInitialSettings.removeAll()
|
|
|
|
if !self.useCaches {
|
|
self.cachedSettings.removeAll()
|
|
}
|
|
}
|
|
}
|
|
}
|