Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
overtake 2021-01-14 13:20:35 +03:00
commit 8fe90f1436
8 changed files with 299 additions and 62 deletions

View File

@ -2,16 +2,18 @@ import Postbox
public struct ExportedInvitation: PostboxCoding, Equatable {
public let link: String
public let revoked: Bool
public let isPermanent: Bool
public let isRevoked: Bool
public let adminId: PeerId
public let date: Int32
public let expireDate: Int32?
public let usageLimit: Int32?
public let count: Int32?
public init(link: String, revoked: Bool, adminId: PeerId, date: Int32, expireDate: Int32?, usageLimit: Int32?, count: Int32?) {
public init(link: String, isPermanent: Bool, isRevoked: Bool, adminId: PeerId, date: Int32, expireDate: Int32?, usageLimit: Int32?, count: Int32?) {
self.link = link
self.revoked = revoked
self.isPermanent = isPermanent
self.isRevoked = isRevoked
self.adminId = adminId
self.date = date
self.expireDate = expireDate
@ -21,7 +23,8 @@ public struct ExportedInvitation: PostboxCoding, Equatable {
public init(decoder: PostboxDecoder) {
self.link = decoder.decodeStringForKey("l", orElse: "")
self.revoked = decoder.decodeBoolForKey("revoked", orElse: false)
self.isPermanent = decoder.decodeBoolForKey("permanent", orElse: false)
self.isRevoked = decoder.decodeBoolForKey("revoked", orElse: false)
self.adminId = PeerId(decoder.decodeInt64ForKey("adminId", orElse: 0))
self.date = decoder.decodeInt32ForKey("date", orElse: 0)
self.expireDate = decoder.decodeOptionalInt32ForKey("expireDate")
@ -31,7 +34,8 @@ public struct ExportedInvitation: PostboxCoding, Equatable {
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeString(self.link, forKey: "l")
encoder.encodeBool(self.revoked, forKey: "revoked")
encoder.encodeBool(self.isPermanent, forKey: "permanent")
encoder.encodeBool(self.isRevoked, forKey: "revoked")
encoder.encodeInt64(self.adminId.toInt64(), forKey: "adminId")
encoder.encodeInt32(self.date, forKey: "date")
if let expireDate = self.expireDate {
@ -52,6 +56,6 @@ public struct ExportedInvitation: PostboxCoding, Equatable {
}
public static func ==(lhs: ExportedInvitation, rhs: ExportedInvitation) -> Bool {
return lhs.link == rhs.link && lhs.revoked == rhs.revoked && lhs.adminId == rhs.adminId && lhs.date == rhs.date && lhs.expireDate == rhs.expireDate && lhs.usageLimit == rhs.usageLimit && lhs.count == rhs.count
return lhs.link == rhs.link && lhs.isPermanent == rhs.isPermanent && lhs.isRevoked == rhs.isRevoked && lhs.adminId == rhs.adminId && lhs.date == rhs.date && lhs.expireDate == rhs.expireDate && lhs.usageLimit == rhs.usageLimit && lhs.count == rhs.count
}
}

View File

@ -73,6 +73,7 @@ public struct Namespaces {
public static let cachedPollResults: Int8 = 9
public static let cachedContextResults: Int8 = 10
public static let proximityNotificationStoredState: Int8 = 11
public static let cachedPeerInvitationImporters: Int8 = 12
}
public struct UnorderedItemList {

View File

@ -150,7 +150,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1649296275] = { return Api.Peer.parse_peerUser($0) }
dict[-1160714821] = { return Api.Peer.parse_peerChat($0) }
dict[-1109531342] = { return Api.Peer.parse_peerChannel($0) }
dict[-1748638807] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) }
dict[410107472] = { return Api.messages.ExportedChatInvite.parse_exportedChatInvite($0) }
dict[-1868808300] = { return Api.PaymentRequestedInfo.parse_paymentRequestedInfo($0) }
dict[164646985] = { return Api.UserStatus.parse_userStatusEmpty($0) }
dict[-306628279] = { return Api.UserStatus.parse_userStatusOnline($0) }

View File

@ -221,21 +221,16 @@ public struct messages {
}
public enum ExportedChatInvite: TypeConstructorDescription {
case exportedChatInvite(invite: Api.ExportedChatInvite, recentImporters: [Int32], users: [Api.User])
case exportedChatInvite(invite: Api.ExportedChatInvite, users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .exportedChatInvite(let invite, let recentImporters, let users):
case .exportedChatInvite(let invite, let users):
if boxed {
buffer.appendInt32(-1748638807)
buffer.appendInt32(410107472)
}
invite.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(recentImporters.count))
for item in recentImporters {
serializeInt32(item, buffer: buffer, boxed: false)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
@ -246,8 +241,8 @@ public struct messages {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .exportedChatInvite(let invite, let recentImporters, let users):
return ("exportedChatInvite", [("invite", invite), ("recentImporters", recentImporters), ("users", users)])
case .exportedChatInvite(let invite, let users):
return ("exportedChatInvite", [("invite", invite), ("users", users)])
}
}
@ -256,19 +251,14 @@ public struct messages {
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
var _2: [Int32]?
var _2: [Api.User]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
}
var _3: [Api.User]?
if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, recentImporters: _2!, users: _3!)
if _c1 && _c2 {
return Api.messages.ExportedChatInvite.exportedChatInvite(invite: _1!, users: _2!)
}
else {
return nil

View File

@ -3903,23 +3903,6 @@ public extension Api {
})
}
public static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(347716823)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", flags), ("peer", peer), ("expireDate", expireDate), ("usageLimit", usageLimit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in
let reader = BufferReader(buffer)
var result: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
return result
})
}
public static func getExportedChatInvites(flags: Int32, peer: Api.InputPeer, adminId: Api.InputUser?, offsetLink: String?, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvites>) {
let buffer = Buffer()
buffer.appendInt32(1838984707)
@ -3938,6 +3921,23 @@ public extension Api {
})
}
public static func exportChatInvite(flags: Int32, peer: Api.InputPeer, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(347716823)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.exportChatInvite", parameters: [("flags", flags), ("peer", peer), ("expireDate", expireDate), ("usageLimit", usageLimit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.ExportedChatInvite? in
let reader = BufferReader(buffer)
var result: Api.ExportedChatInvite?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.ExportedChatInvite
}
return result
})
}
public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(48562110)
@ -3956,21 +3956,6 @@ public extension Api {
})
}
public static func getExportedChatInvite(peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(1937010524)
peer.serialize(buffer, true)
serializeString(link, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getExportedChatInvite", parameters: [("peer", peer), ("link", link)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in
let reader = BufferReader(buffer)
var result: Api.messages.ExportedChatInvite?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.ExportedChatInvite
}
return result
})
}
public static func getChatInviteImporters(peer: Api.InputPeer, link: String, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ChatInviteImporters>) {
let buffer = Buffer()
buffer.appendInt32(654013065)

View File

@ -169,6 +169,7 @@ private var declaredEncodables: Void = {
declareEncodable(CountriesList.self, f: { CountriesList(decoder: $0) })
declareEncodable(ValidationMessageAttribute.self, f: { ValidationMessageAttribute(decoder: $0) })
declareEncodable(EmojiSearchQueryMessageAttribute.self, f: { EmojiSearchQueryMessageAttribute(decoder: $0) })
declareEncodable(CachedPeerInvitationImporters.self, f: { CachedPeerInvitationImporters(decoder: $0) })
return
}()

View File

@ -10,7 +10,7 @@ extension ExportedInvitation {
case .chatInviteEmpty:
return nil
case let .chatInviteExported(flags, link, adminId, date, expireDate, usageLimit, usage):
self = ExportedInvitation(link: link, revoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), date: date, expireDate: expireDate, usageLimit: usageLimit, count: usage)
self = ExportedInvitation(link: link, isPermanent: (flags & (1 << 5)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: adminId), date: date, expireDate: expireDate, usageLimit: usageLimit, count: usage)
}
}
}

View File

@ -9,11 +9,12 @@ import SyncCore
public func ensuredExistingPeerExportedInvitation(account: Account, peerId: PeerId, revokeExisted: Bool = false) -> Signal<ExportedInvitation?, NoError> {
return account.postbox.transaction { transaction -> Signal<ExportedInvitation?, NoError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var flags: Int32 = (1 << 2)
if let _ = peer as? TelegramChannel {
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData, cachedData.exportedInvitation != nil && !revokeExisted {
return .complete()
} else {
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil))
|> retryRequest
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
return account.postbox.transaction { transaction -> ExportedInvitation? in
@ -36,7 +37,7 @@ public func ensuredExistingPeerExportedInvitation(account: Account, peerId: Peer
if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedGroupData, cachedData.exportedInvitation != nil && !revokeExisted {
return .complete()
} else {
return account.network.request(Api.functions.messages.exportChatInvite(flags: 0, peer: inputPeer, expireDate: nil, usageLimit: nil))
return account.network.request(Api.functions.messages.exportChatInvite(flags: flags, peer: inputPeer, expireDate: nil, usageLimit: nil))
|> retryRequest
|> mapToSignal { result -> Signal<ExportedInvitation?, NoError> in
return account.postbox.transaction { transaction -> ExportedInvitation? in
@ -146,7 +147,7 @@ public func editPeerExportedInvitation(account: Account, peerId: PeerId, link: S
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: expireDate, usageLimit: usageLimit))
|> mapError { _ in return EditPeerExportedInvitationError.generic }
|> map { result -> ExportedInvitation? in
if case let .exportedChatInvite(invite, recentImporters, users) = result {
if case let .exportedChatInvite(invite, users) = result {
var peers: [Peer] = []
for user in users {
let telegramUser = TelegramUser(user: user)
@ -186,3 +187,258 @@ public func revokePeerExportedInvitation(account: Account, peerId: PeerId, link:
|> castError(RevokePeerExportedInvitationError.self)
|> switchToLatest
}
private let cachedPeerInvitationImportersCollectionSpec = ItemCacheCollectionSpec(lowWaterItemCount: 10, highWaterItemCount: 20)
public struct PeerInvitationImportersState: Equatable {
public struct Importer: Equatable {
public var peer: RenderedPeer
public var date: Int32
}
public var importers: [Importer]
public var isLoadingMore: Bool
public var hasLoadedOnce: Bool
public var canLoadMore: Bool
public var count: Int
}
final class CachedPeerInvitationImporters: PostboxCoding {
let peerIds: [PeerId]
let dates: [PeerId: Int32]
let count: Int32
public static func key(peerId: PeerId, link: String) -> ValueBoxKey {
let key = ValueBoxKey(length: 8 + 4)
key.setInt64(0, value: peerId.toInt64())
key.setInt32(8, value: Int32(HashFunctions.murMurHash32(link)))
return key
}
init(importers: [PeerInvitationImportersState.Importer], count: Int32) {
self.peerIds = importers.map { $0.peer.peerId }
self.dates = importers.reduce(into: [PeerId: Int32]()) {
$0[$1.peer.peerId] = $1.date
}
self.count = count
}
public init(peerIds: [PeerId], dates: [PeerId: Int32], count: Int32) {
self.peerIds = peerIds
self.dates = dates
self.count = count
}
public init(decoder: PostboxDecoder) {
self.peerIds = decoder.decodeInt64ArrayForKey("peerIds").map(PeerId.init)
var dates: [PeerId: Int32] = [:]
let datesArray = decoder.decodeInt32ArrayForKey("dates")
for index in stride(from: 0, to: datesArray.endIndex, by: 2) {
let userId = datesArray[index]
let date = datesArray[index + 1]
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
dates[peerId] = date
}
self.dates = dates
self.count = decoder.decodeInt32ForKey("count", orElse: 0)
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeInt64Array(self.peerIds.map { $0.toInt64() }, forKey: "peerIds")
var dates: [Int32] = []
for (peerId, date) in self.dates {
dates.append(peerId.id)
dates.append(date)
}
encoder.encodeInt32Array(dates, forKey: "dates")
encoder.encodeInt32(self.count, forKey: "count")
}
}
private final class PeerInvitationImportersContextImpl {
private let queue: Queue
private let account: Account
private let peerId: PeerId
private let link: String
private let disposable = MetaDisposable()
private var isLoadingMore: Bool = false
private var hasLoadedOnce: Bool = false
private var canLoadMore: Bool = true
private var results: [PeerInvitationImportersState.Importer] = []
private var count: Int
private var populateCache: Bool = true
let state = Promise<PeerInvitationImportersState>()
init(queue: Queue, account: Account, peerId: PeerId, invite: ExportedInvitation) {
self.queue = queue
self.account = account
self.peerId = peerId
self.link = invite.link
let count = invite.count.flatMap { Int($0) } ?? 0
self.count = count
self.isLoadingMore = true
self.disposable.set((account.postbox.transaction { transaction -> (peers: [PeerInvitationImportersState.Importer], canLoadMore: Bool)? in
let cachedResult = transaction.retrieveItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerInvitationImporters.key(peerId: peerId, link: invite.link))) as? CachedPeerInvitationImporters
if let cachedResult = cachedResult, Int(cachedResult.count) == count {
var result: [PeerInvitationImportersState.Importer] = []
for peerId in cachedResult.peerIds {
if let peer = transaction.getPeer(peerId), let date = cachedResult.dates[peerId] {
result.append(PeerInvitationImportersState.Importer(peer: RenderedPeer(peer: peer), date: date))
} else {
return nil
}
}
return (result, Int(cachedResult.count) > result.count)
} else {
return nil
}
}
|> deliverOn(self.queue)).start(next: { [weak self] cachedPeersAndCanLoadMore in
guard let strongSelf = self else {
return
}
strongSelf.isLoadingMore = false
if let (cachedPeers, canLoadMore) = cachedPeersAndCanLoadMore {
strongSelf.results = cachedPeers
strongSelf.hasLoadedOnce = true
strongSelf.canLoadMore = canLoadMore
}
strongSelf.loadMore()
}))
self.loadMore()
}
deinit {
self.disposable.dispose()
}
func loadMore() {
if self.isLoadingMore {
return
}
self.isLoadingMore = true
let account = self.account
let peerId = self.peerId
let link = self.link
let lastResult = self.results.last
let populateCache = self.populateCache
self.disposable.set((self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<([PeerInvitationImportersState.Importer], Int), NoError> in
if let inputPeer = inputPeer {
let offsetUser = lastResult?.peer.peer.flatMap { apiInputUser($0) } ?? .inputUserEmpty
let offsetDate = lastResult?.date ?? 0
let signal = account.network.request(Api.functions.messages.getChatInviteImporters(peer: inputPeer, link: link, offsetDate: offsetDate, offsetUser: offsetUser, limit: lastResult == nil ? 10 : 50))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.ChatInviteImporters?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<([PeerInvitationImportersState.Importer], Int), NoError> in
return account.postbox.transaction { transaction -> ([PeerInvitationImportersState.Importer], Int) in
guard let result = result else {
return ([], 0)
}
switch result {
case let .chatInviteImporters(count, importers, users):
var peers: [Peer] = []
for apiUser in users {
peers.append(TelegramUser(user: apiUser))
}
updatePeers(transaction: transaction, peers: peers, update: { _, updated in
return updated
})
var resultImporters: [PeerInvitationImportersState.Importer] = []
for importer in importers {
let peerId: PeerId
let date: Int32
switch importer {
case let .chatInviteImporter(userId, dateValue):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
date = dateValue
}
if let peer = transaction.getPeer(peerId) {
resultImporters.append(PeerInvitationImportersState.Importer(peer: RenderedPeer(peer: peer), date: date))
}
}
if populateCache {
transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedPeerInvitationImporters, key: CachedPeerInvitationImporters.key(peerId: peerId, link: link)), entry: CachedPeerInvitationImporters(importers: resultImporters, count: count), collectionSpec: cachedPeerInvitationImportersCollectionSpec)
}
return (resultImporters, Int(count))
}
}
}
return signal
} else {
return .single(([], 0))
}
}
|> deliverOn(self.queue)).start(next: { [weak self] importers, updatedCount in
guard let strongSelf = self else {
return
}
if strongSelf.populateCache {
strongSelf.populateCache = false
strongSelf.results.removeAll()
}
var existingIds = Set(strongSelf.results.map { $0.peer.peerId })
for importer in importers {
if !existingIds.contains(importer.peer.peerId) {
strongSelf.results.append(importer)
existingIds.insert(importer.peer.peerId)
}
}
strongSelf.isLoadingMore = false
strongSelf.hasLoadedOnce = true
strongSelf.canLoadMore = !importers.isEmpty
if strongSelf.canLoadMore {
strongSelf.count = max(updatedCount, strongSelf.results.count)
} else {
strongSelf.count = strongSelf.results.count
}
strongSelf.updateState()
}))
self.updateState()
}
private func updateState() {
self.state.set(.single(PeerInvitationImportersState(importers: self.results, isLoadingMore: self.isLoadingMore, hasLoadedOnce: self.hasLoadedOnce, canLoadMore: self.canLoadMore, count: self.count)))
}
}
public final class PeerInvitationImportersContext {
private let queue: Queue = Queue()
private let impl: QueueLocalObject<PeerInvitationImportersContextImpl>
public var state: Signal<PeerInvitationImportersState, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.state.get().start(next: { value in
subscriber.putNext(value)
}))
}
return disposable
}
}
public init(account: Account, peerId: PeerId, invite: ExportedInvitation) {
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return PeerInvitationImportersContextImpl(queue: queue, account: account, peerId: peerId, invite: invite)
})
}
public func loadMore() {
self.impl.with { impl in
impl.loadMore()
}
}
}