mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
@@ -872,14 +872,29 @@ private func loadAndStorePeerChatInfos(accountPeerId: PeerId, postbox: Postbox,
|
||||
}
|
||||
|
||||
struct ChatListFiltersState: Codable, Equatable {
|
||||
struct ChatListFilterUpdates: Codable, Equatable {
|
||||
var folderId: Int32
|
||||
var timestamp: Int32
|
||||
var peerIds: [PeerId]
|
||||
|
||||
init(folderId: Int32, timestamp: Int32, peerIds: [PeerId]) {
|
||||
self.folderId = folderId
|
||||
self.timestamp = timestamp
|
||||
self.peerIds = peerIds
|
||||
}
|
||||
}
|
||||
|
||||
var filters: [ChatListFilter]
|
||||
var remoteFilters: [ChatListFilter]?
|
||||
|
||||
static var `default` = ChatListFiltersState(filters: [], remoteFilters: nil)
|
||||
var updates: [ChatListFilterUpdates]
|
||||
|
||||
fileprivate init(filters: [ChatListFilter], remoteFilters: [ChatListFilter]?) {
|
||||
static var `default` = ChatListFiltersState(filters: [], remoteFilters: nil, updates: [])
|
||||
|
||||
fileprivate init(filters: [ChatListFilter], remoteFilters: [ChatListFilter]?, updates: [ChatListFilterUpdates]) {
|
||||
self.filters = filters
|
||||
self.remoteFilters = remoteFilters
|
||||
self.updates = updates
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@@ -887,6 +902,7 @@ struct ChatListFiltersState: Codable, Equatable {
|
||||
|
||||
self.filters = try container.decode([ChatListFilter].self, forKey: "filters")
|
||||
self.remoteFilters = try container.decodeIfPresent([ChatListFilter].self, forKey: "remoteFilters")
|
||||
self.updates = try container.decodeIfPresent([ChatListFilterUpdates].self, forKey: "updates") ?? []
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
@@ -894,6 +910,14 @@ struct ChatListFiltersState: Codable, Equatable {
|
||||
|
||||
try container.encode(self.filters, forKey: "filters")
|
||||
try container.encodeIfPresent(self.remoteFilters, forKey: "remoteFilters")
|
||||
try container.encode(self.updates, forKey: "updates")
|
||||
}
|
||||
|
||||
mutating func normalize() {
|
||||
if self.updates.isEmpty {
|
||||
return
|
||||
}
|
||||
self.updates.removeAll(where: { update in !self.filters.contains(where: { $0.id == update.folderId }) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,6 +942,9 @@ func _internal_updateChatListFiltersInteractively(postbox: Postbox, _ f: @escapi
|
||||
hasUpdates = true
|
||||
}
|
||||
updated = updatedFilters
|
||||
|
||||
state.normalize()
|
||||
|
||||
return PreferencesEntry(state)
|
||||
})
|
||||
if hasUpdates {
|
||||
@@ -936,6 +963,7 @@ func _internal_updateChatListFiltersInteractively(transaction: Transaction, _ f:
|
||||
state.filters = updatedFilters
|
||||
hasUpdates = true
|
||||
}
|
||||
state.normalize()
|
||||
return PreferencesEntry(state)
|
||||
})
|
||||
if hasUpdates {
|
||||
@@ -943,7 +971,6 @@ func _internal_updateChatListFiltersInteractively(transaction: Transaction, _ f:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func _internal_updatedChatListFilters(postbox: Postbox) -> Signal<[ChatListFilter], NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> [ChatListFilter] in
|
||||
@@ -953,6 +980,15 @@ func _internal_updatedChatListFilters(postbox: Postbox) -> Signal<[ChatListFilte
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
func _internal_updatedChatListFiltersState(postbox: Postbox) -> Signal<ChatListFiltersState, NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> ChatListFiltersState in
|
||||
let filtersState = preferences.values[PreferencesKeys.chatListFilters]?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
return filtersState
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
}
|
||||
|
||||
func _internal_updatedChatListFiltersInfo(postbox: Postbox) -> Signal<(filters: [ChatListFilter], synchronized: Bool), NoError> {
|
||||
return postbox.preferencesView(keys: [PreferencesKeys.chatListFilters])
|
||||
|> map { preferences -> (filters: [ChatListFilter], synchronized: Bool) in
|
||||
@@ -982,11 +1018,17 @@ func _internal_currentChatListFilters(transaction: Transaction) -> [ChatListFilt
|
||||
return settings.filters
|
||||
}
|
||||
|
||||
func _internal_currentChatListFiltersState(transaction: Transaction) -> ChatListFiltersState {
|
||||
let settings = transaction.getPreferencesEntry(key: PreferencesKeys.chatListFilters)?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
return settings
|
||||
}
|
||||
|
||||
func updateChatListFiltersState(transaction: Transaction, _ f: (ChatListFiltersState) -> ChatListFiltersState) -> ChatListFiltersState {
|
||||
var result: ChatListFiltersState?
|
||||
transaction.updatePreferencesEntry(key: PreferencesKeys.chatListFilters, { entry in
|
||||
let settings = entry?.get(ChatListFiltersState.self) ?? ChatListFiltersState.default
|
||||
let updated = f(settings)
|
||||
var updated = f(settings)
|
||||
updated.normalize()
|
||||
result = updated
|
||||
return PreferencesEntry(updated)
|
||||
})
|
||||
|
||||
@@ -60,7 +60,7 @@ func _internal_exportChatFolder(account: Account, filterId: Int32, title: String
|
||||
|> mapToSignal { inputPeers -> Signal<ExportedChatFolderLink, ExportChatFolderError> in
|
||||
return account.network.request(Api.functions.communities.exportCommunityInvite(community: .inputCommunityDialogFilter(filterId: filterId), title: title, peers: inputPeers))
|
||||
|> `catch` { error -> Signal<Api.communities.ExportedCommunityInvite, ExportChatFolderError> in
|
||||
if error.errorDescription == "INVITES_TOO_MUCH" {
|
||||
if error.errorDescription == "INVITES_TOO_MUCH" || error.errorDescription == "FILTERS_TOO_MUCH" {
|
||||
return account.postbox.transaction { transaction -> (AppConfiguration, Bool) in
|
||||
return (currentAppConfiguration(transaction: transaction), transaction.getPeer(account.peerId)?.isPremium ?? false)
|
||||
}
|
||||
@@ -69,10 +69,18 @@ func _internal_exportChatFolder(account: Account, filterId: Int32, title: String
|
||||
let userDefaultLimits = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: false)
|
||||
let userPremiumLimits = UserLimitsConfiguration(appConfiguration: appConfiguration, isPremium: true)
|
||||
|
||||
if isPremium {
|
||||
return .fail(.limitExceeded(limit: userPremiumLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
if error.errorDescription == "FILTERS_TOO_MUCH" {
|
||||
if isPremium {
|
||||
return .fail(.limitExceeded(limit: userPremiumLimits.maxSharedFolderJoin, premiumLimit: userPremiumLimits.maxSharedFolderJoin))
|
||||
} else {
|
||||
return .fail(.limitExceeded(limit: userDefaultLimits.maxSharedFolderJoin, premiumLimit: userPremiumLimits.maxSharedFolderJoin))
|
||||
}
|
||||
} else {
|
||||
return .fail(.limitExceeded(limit: userDefaultLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
if isPremium {
|
||||
return .fail(.limitExceeded(limit: userPremiumLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
} else {
|
||||
return .fail(.limitExceeded(limit: userDefaultLimits.maxSharedFolderInviteLinks, premiumLimit: userPremiumLimits.maxSharedFolderInviteLinks))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -246,7 +254,9 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal<Cha
|
||||
|> mapToSignal { result -> Signal<ChatFolderLinkContents, CheckChatFolderLinkError> in
|
||||
return account.postbox.transaction { transaction -> ChatFolderLinkContents in
|
||||
switch result {
|
||||
case let .communityInvite(title, peers, chats, users):
|
||||
case let .communityInvite(_, title, emoticon, peers, chats, users):
|
||||
let _ = emoticon
|
||||
|
||||
var allPeers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
@@ -321,7 +331,7 @@ func _internal_checkChatFolderLink(account: Account, slug: String) -> Signal<Cha
|
||||
if let peerValue = transaction.getPeer(peer.peerId) {
|
||||
resultPeers.append(EnginePeer(peerValue))
|
||||
|
||||
if transaction.getPeerChatListIndex(peer.peerId) != nil {
|
||||
if currentFilterPeers.contains(where: { $0 == peer.peerId }) && transaction.getPeerChatListIndex(peer.peerId) != nil {
|
||||
alreadyMemberPeerIds.insert(peer.peerId)
|
||||
}
|
||||
}
|
||||
@@ -407,74 +417,148 @@ func _internal_joinChatFolderLink(account: Account, slug: String, peerIds: [Engi
|
||||
public final class ChatFolderUpdates: Equatable {
|
||||
fileprivate let folderId: Int32
|
||||
fileprivate let title: String
|
||||
fileprivate let missingPeers: [Api.Peer]
|
||||
fileprivate let chats: [Api.Chat]
|
||||
fileprivate let users: [Api.User]
|
||||
fileprivate let missingPeers: [EnginePeer]
|
||||
|
||||
public var availableChatsToJoin: Int {
|
||||
return self.missingPeers.count
|
||||
}
|
||||
|
||||
public var chatFolderLinkContents: ChatFolderLinkContents {
|
||||
var peers: [EnginePeer] = []
|
||||
for missingPeer in self.missingPeers {
|
||||
for chat in chats {
|
||||
if chat.peerId == missingPeer.peerId {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ChatFolderLinkContents(localFilterId: self.folderId, title: self.title, peers: peers, alreadyMemberPeerIds: Set())
|
||||
return ChatFolderLinkContents(localFilterId: self.folderId, title: self.title, peers: self.missingPeers, alreadyMemberPeerIds: Set())
|
||||
}
|
||||
|
||||
fileprivate init(
|
||||
folderId: Int32,
|
||||
title: String,
|
||||
missingPeers: [Api.Peer],
|
||||
chats: [Api.Chat],
|
||||
users: [Api.User]
|
||||
missingPeers: [EnginePeer]
|
||||
) {
|
||||
self.folderId = folderId
|
||||
self.title = title
|
||||
self.missingPeers = missingPeers
|
||||
self.chats = chats
|
||||
self.users = users
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatFolderUpdates, rhs: ChatFolderUpdates) -> Bool {
|
||||
if lhs.folderId != rhs.folderId {
|
||||
return false
|
||||
}
|
||||
if lhs.missingPeers.map(\.peerId) != rhs.missingPeers.map(\.peerId) {
|
||||
if lhs.missingPeers.map(\.id) != rhs.missingPeers.map(\.id) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_getChatFolderUpdates(account: Account, folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
return account.network.request(Api.functions.communities.getCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.communities.CommunityUpdates?, NoError> in
|
||||
return .single(nil)
|
||||
func _internal_pollChatFolderUpdatesOnce(account: Account, folderId: Int32) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> ChatListFiltersState in
|
||||
return _internal_currentChatListFiltersState(transaction: transaction)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<ChatFolderUpdates?, NoError> in
|
||||
guard let result = result else {
|
||||
|> mapToSignal { state -> Signal<Never, NoError> in
|
||||
let timestamp = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if let current = state.updates.first(where: { $0.folderId == folderId }) {
|
||||
let updateInterval: Int32
|
||||
#if DEBUG
|
||||
updateInterval = 5
|
||||
#else
|
||||
updateInterval = 60 * 60
|
||||
#endif
|
||||
|
||||
if current.timestamp + updateInterval >= timestamp {
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.communities.getCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.communities.CommunityUpdates?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
switch result {
|
||||
case let .communityUpdates(missingPeers, chats, users):
|
||||
return account.postbox.transaction { transaction -> ChatFolderUpdates? in
|
||||
for filter in _internal_currentChatListFilters(transaction: transaction) {
|
||||
if case let .filter(id, title, _, _) = filter, id == folderId {
|
||||
return ChatFolderUpdates(folderId: folderId, title: title, missingPeers: missingPeers, chats: chats, users: users)
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
||||
guard let result = result else {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
state.updates.append(ChatListFiltersState.ChatListFilterUpdates(folderId: folderId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970), peerIds: []))
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
return nil
|
||||
|> ignoreValues
|
||||
}
|
||||
switch result {
|
||||
case let .communityUpdates(missingPeers, chats, users):
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: Api.User] = [:]
|
||||
|
||||
for user in users {
|
||||
let telegramUser = TelegramUser(user: user)
|
||||
peers.append(telegramUser)
|
||||
peerPresences[telegramUser.id] = user
|
||||
}
|
||||
for chat in chats {
|
||||
if let peer = parseTelegramGroupOrChannel(chat: chat) {
|
||||
peers.append(peer)
|
||||
}
|
||||
}
|
||||
|
||||
updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in
|
||||
return updated
|
||||
})
|
||||
updatePeerPresences(transaction: transaction, accountPeerId: account.peerId, peerPresences: peerPresences)
|
||||
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
state.updates.append(ChatListFiltersState.ChatListFilterUpdates(folderId: folderId, timestamp: Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970), peerIds: missingPeers.map(\.peerId)))
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_subscribedChatFolderUpdates(account: Account, folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
struct InternalData: Equatable {
|
||||
var title: String
|
||||
var peerIds: [EnginePeer.Id]
|
||||
}
|
||||
|
||||
return _internal_updatedChatListFiltersState(postbox: account.postbox)
|
||||
|> map { state -> InternalData? in
|
||||
guard let update = state.updates.first(where: { $0.folderId == folderId }) else {
|
||||
return nil
|
||||
}
|
||||
guard let folder = state.filters.first(where: { $0.id == folderId }) else {
|
||||
return nil
|
||||
}
|
||||
guard case let .filter(_, title, _, data) = folder, data.isShared else {
|
||||
return nil
|
||||
}
|
||||
let filteredPeerIds: [PeerId] = update.peerIds.filter { !data.includePeers.peers.contains($0) }
|
||||
return InternalData(title: title, peerIds: filteredPeerIds)
|
||||
}
|
||||
|> distinctUntilChanged
|
||||
|> mapToSignal { internalData -> Signal<ChatFolderUpdates?, NoError> in
|
||||
guard let internalData = internalData else {
|
||||
return .single(nil)
|
||||
}
|
||||
if internalData.peerIds.isEmpty {
|
||||
return .single(nil)
|
||||
}
|
||||
return account.postbox.transaction { transaction -> ChatFolderUpdates? in
|
||||
var peers: [EnginePeer] = []
|
||||
for peerId in internalData.peerIds {
|
||||
if let peer = transaction.getPeer(peerId) {
|
||||
peers.append(EnginePeer(peer))
|
||||
}
|
||||
}
|
||||
return ChatFolderUpdates(folderId: folderId, title: internalData.title, missingPeers: peers)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,11 +614,22 @@ func _internal_joinAvailableChatsInFolder(account: Account, updates: ChatFolderU
|
||||
}
|
||||
|
||||
func _internal_hideChatFolderUpdates(account: Account, folderId: Int32) -> Signal<Never, NoError> {
|
||||
return account.network.request(Api.functions.communities.hideCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
let _ = updateChatListFiltersState(transaction: transaction, { state in
|
||||
var state = state
|
||||
|
||||
state.updates.removeAll(where: { $0.folderId == folderId })
|
||||
|
||||
return state
|
||||
})
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Never, NoError> in
|
||||
return account.network.request(Api.functions.communities.hideCommunityUpdates(community: .inputCommunityDialogFilter(filterId: folderId)))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
func _internal_leaveChatFolder(account: Account, folderId: Int32, removePeerIds: [EnginePeer.Id]) -> Signal<Never, NoError> {
|
||||
|
||||
@@ -1058,8 +1058,20 @@ public extension TelegramEngine {
|
||||
return _internal_joinChatFolderLink(account: self.account, slug: slug, peerIds: peerIds)
|
||||
}
|
||||
|
||||
public func getChatFolderUpdates(folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
return _internal_getChatFolderUpdates(account: self.account, folderId: folderId)
|
||||
public func pollChatFolderUpdates(folderId: Int32) -> Signal<Never, NoError> {
|
||||
let signal = _internal_pollChatFolderUpdatesOnce(account: self.account, folderId: folderId)
|
||||
return (
|
||||
signal
|
||||
|> then(
|
||||
Signal<Never, NoError>.complete()
|
||||
|> delay(10.0, queue: .concurrentDefaultQueue())
|
||||
)
|
||||
)
|
||||
|> restart
|
||||
}
|
||||
|
||||
public func subscribedChatFolderUpdates(folderId: Int32) -> Signal<ChatFolderUpdates?, NoError> {
|
||||
return _internal_subscribedChatFolderUpdates(account: self.account, folderId: folderId)
|
||||
}
|
||||
|
||||
public func joinAvailableChatsInFolder(updates: ChatFolderUpdates, peerIds: [EnginePeer.Id]) -> Signal<Never, JoinChatFolderLinkError> {
|
||||
|
||||
Reference in New Issue
Block a user