mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
160 lines
5.8 KiB
Swift
160 lines
5.8 KiB
Swift
import Foundation
|
|
import Postbox
|
|
import TelegramCore
|
|
import SwiftSignalKit
|
|
|
|
private struct FetchManagerLocationEntryId: Hashable {
|
|
let resourceId: MediaResourceId
|
|
let locationKey: FetchManagerLocationKey
|
|
|
|
static func ==(lhs: FetchManagerLocationEntryId, rhs: FetchManagerLocationEntryId) -> Bool {
|
|
if !lhs.resourceId.isEqual(to: rhs.resourceId) {
|
|
return false
|
|
}
|
|
if !lhs.locationKey.isEqual(to: rhs.locationKey) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
var hashValue: Int {
|
|
return self.resourceId.hashValue &* 31 &+ self.locationKey.hashValue
|
|
}
|
|
}
|
|
|
|
private final class FetchManagerLocationEntry {
|
|
let id: FetchManagerLocationEntryId
|
|
let resource: MediaResource
|
|
|
|
var referenceCount: Int32 = 0
|
|
var elevatedPriorityReferenceCount: Int32 = 0
|
|
var userInitiatedPriorityIndices: [Int32] = []
|
|
|
|
var priorityKey: FetchManagerPriorityKey? {
|
|
if self.referenceCount > 0 {
|
|
return FetchManagerPriorityKey(locationKey: self.id.locationKey, hasElevatedPriority: self.elevatedPriorityReferenceCount > 0, userInitiatedPriority: userInitiatedPriorityIndices.last)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
init(id: FetchManagerLocationEntryId, resource: MediaResource) {
|
|
self.id = id
|
|
self.resource = resource
|
|
}
|
|
}
|
|
|
|
private final class FetchManagerCategoryLocationContext {
|
|
private var topEntryIdAndPriority: (FetchManagerLocationEntryId, FetchManagerPriorityKey)?
|
|
private var entries: [FetchManagerLocationEntryId: FetchManagerLocationEntry] = [:]
|
|
|
|
func withEntry(id: FetchManagerLocationEntryId, resource: MediaResource, _ f: (FetchManagerLocationEntry) -> Void) {
|
|
let entry: FetchManagerLocationEntry
|
|
let previousPriorityKey: FetchManagerPriorityKey?
|
|
if let current = self.entries[id] {
|
|
entry = current
|
|
previousPriorityKey = entry.priorityKey
|
|
} else {
|
|
previousPriorityKey = nil
|
|
entry = FetchManagerLocationEntry(id: id, resource: resource)
|
|
self.entries[id] = entry
|
|
}
|
|
|
|
f(entry)
|
|
|
|
let updatedPriorityKey = entry.priorityKey
|
|
if previousPriorityKey != updatedPriorityKey {
|
|
if let updatedPriorityKey = updatedPriorityKey {
|
|
if let (topId, topPriority) = self.topEntryIdAndPriority {
|
|
if updatedPriorityKey < topPriority {
|
|
self.topEntryIdAndPriority = (entry.id, updatedPriorityKey)
|
|
} else if updatedPriorityKey > topPriority && topId == id {
|
|
self.topEntryIdAndPriority = nil
|
|
}
|
|
} else {
|
|
self.topEntryIdAndPriority = (entry.id, updatedPriorityKey)
|
|
}
|
|
} else {
|
|
if self.topEntryIdAndPriority?.0 == id {
|
|
self.topEntryIdAndPriority = nil
|
|
}
|
|
self.entries.removeValue(forKey: id)
|
|
}
|
|
}
|
|
|
|
if self.topEntryIdAndPriority == nil && !self.entries.isEmpty {
|
|
var topEntryIdAndPriority: (FetchManagerLocationEntryId, FetchManagerPriorityKey)?
|
|
for (id, entry) in self.entries {
|
|
if let entryPriorityKey = entry.priorityKey {
|
|
if let (_, topKey) = topEntryIdAndPriority {
|
|
if entryPriorityKey < topKey {
|
|
topEntryIdAndPriority = (id, entryPriorityKey)
|
|
}
|
|
} else {
|
|
topEntryIdAndPriority = (id, entryPriorityKey)
|
|
}
|
|
} else {
|
|
assertionFailure()
|
|
}
|
|
}
|
|
|
|
self.topEntryIdAndPriority = topEntryIdAndPriority
|
|
}
|
|
}
|
|
|
|
var isEmpty: Bool {
|
|
return self.entries.isEmpty
|
|
}
|
|
}
|
|
|
|
final class FetchManager {
|
|
private let queue = Queue()
|
|
private let network: Network
|
|
|
|
private var categoryLocationContexts: [FetchManagerCategoryLocationKey: FetchManagerCategoryLocationContext] = [:]
|
|
|
|
init(network: Network) {
|
|
self.network = network
|
|
}
|
|
|
|
private func withLocationContext(_ key: FetchManagerCategoryLocationKey, _ f: (FetchManagerCategoryLocationContext) -> Void) {
|
|
assert(self.queue.isCurrent())
|
|
let context: FetchManagerCategoryLocationContext
|
|
if let current = self.categoryLocationContexts[key] {
|
|
context = current
|
|
} else {
|
|
context = FetchManagerCategoryLocationContext()
|
|
self.categoryLocationContexts[key] = context
|
|
}
|
|
|
|
f(context)
|
|
|
|
if context.isEmpty {
|
|
self.categoryLocationContexts.removeValue(forKey: key)
|
|
}
|
|
}
|
|
|
|
func interactivelyFetched(category: FetchManagerCategory, location: FetchManagerLocation, locationKey: FetchManagerLocationKey, resource: MediaResource, elevatedPriority: Bool, userInitiated: Bool) -> Signal<Void, NoError> {
|
|
let queue = self.queue
|
|
return Signal { [weak self] subscriber in
|
|
if let strongSelf = self {
|
|
strongSelf.withLocationContext(FetchManagerCategoryLocationKey(location: location, category: category), { context in
|
|
context.withEntry(id: FetchManagerLocationEntryId(resourceId: resource.id, locationKey: locationKey), resource: resource, { entry in
|
|
|
|
})
|
|
})
|
|
|
|
return ActionDisposable {
|
|
queue.async {
|
|
if let strongSelf = self {
|
|
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
return EmptyDisposable
|
|
}
|
|
} |> runOn(self.queue)
|
|
}
|
|
}
|