diff --git a/TelegramCore/Authorization.swift b/TelegramCore/Authorization.swift index bdc97d47c4..8fd65e1d45 100644 --- a/TelegramCore/Authorization.swift +++ b/TelegramCore/Authorization.swift @@ -444,7 +444,7 @@ public func signUpWithName(account: UnauthorizedAccount, firstName: String, last let resource = LocalFileMediaResource(fileId: arc4random64()) account.postbox.mediaBox.storeResourceData(resource.id, data: avatarData) - return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource)) + return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), mapResourceToAvatarSizes: { _, _ in .single([:]) }) |> `catch` { _ -> Signal in return .complete() } diff --git a/TelegramCore/CreateGroup.swift b/TelegramCore/CreateGroup.swift index 7376cb88c4..decb74c50f 100644 --- a/TelegramCore/CreateGroup.swift +++ b/TelegramCore/CreateGroup.swift @@ -21,8 +21,8 @@ public func createGroup(account: Account, title: String, peerIds: [PeerId]) -> S } return account.network.request(Api.functions.messages.createChat(users: inputUsers, title: title)) |> map(Optional.init) - |> `catch` { _ in - return Signal.single(nil) + |> `catch` { _ -> Signal in + return .single(nil) } |> mapToSignal { updates -> Signal in if let updates = updates { @@ -36,9 +36,9 @@ public func createGroup(account: Account, title: String, peerIds: [PeerId]) -> S |> map { _ in return peerId } - |> timeout(5.0, queue: Queue.concurrentDefaultQueue(), alternate: .single(nil)) + } else { + return .single(nil) } - return .single(nil) } else { return .single(nil) } diff --git a/TelegramCore/Holes.swift b/TelegramCore/Holes.swift index 9793ba0923..a82260b625 100644 --- a/TelegramCore/Holes.swift +++ b/TelegramCore/Holes.swift @@ -39,7 +39,7 @@ enum FetchMessageHistoryHoleSource { } } -func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistoryHoleSource, storeMessages: [StoreMessage], _ f: @escaping (Transaction, [Peer], [StoreMessage]) -> Void) -> Signal { +func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistoryHoleSource, peers: [PeerId: Peer], storeMessages: [StoreMessage], _ f: @escaping (Transaction, [Peer], [StoreMessage]) -> Void) -> Signal { return postbox.transaction { transaction -> Signal in var storedIds = Set() var referencedIds = Set() @@ -53,7 +53,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistor } } referencedIds.subtract(storedIds) - referencedIds = transaction.filterStoredMessageIds(referencedIds) + referencedIds.subtract(transaction.filterStoredMessageIds(referencedIds)) if referencedIds.isEmpty { f(transaction, [], []) @@ -61,7 +61,7 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistor } else { var signals: [Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>] = [] for (peerId, messageIds) in messagesIdsGroupedByPeerId(referencedIds) { - if let peer = transaction.getPeer(peerId) { + if let peer = transaction.getPeer(peerId) ?? peers[peerId] { var signal: Signal? if peerId.namespace == Namespaces.Peer.CloudUser || peerId.namespace == Namespaces.Peer.CloudGroup { signal = source.request(Api.functions.messages.getMessages(id: messageIds.map({ Api.InputMessage.inputMessageID(id: $0.id) }))) @@ -282,6 +282,22 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH chats = [] users = [] } + + var peers: [Peer] = [] + var peerPresences: [PeerId: PeerPresence] = [:] + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers.append(groupOrChannel) + } + } + for user in users { + let telegramUser = TelegramUser(user: user) + peers.append(telegramUser) + if let presence = TelegramUserPresence(apiUser: user) { + peerPresences[telegramUser.id] = presence + } + } + var updatedMaxIndex: MessageIndex? if let maxIndexResult = maxIndexResult { let maxIndexMessages: [Api.Message] @@ -317,7 +333,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH } } - return withResolvedAssociatedMessages(postbox: postbox, source: source, storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in + return withResolvedAssociatedMessages(postbox: postbox, source: source, peers: Dictionary(peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in let fillDirection: HoleFillDirection switch direction { case .UpperToLower: @@ -337,22 +353,7 @@ func fetchMessageHistoryHole(accountPeerId: PeerId, source: FetchMessageHistoryH transaction.fillMultipleHoles(hole, fillType: HoleFill(complete: completeFill, direction: fillDirection), tagMask: tagMask, messages: storeMessages) let _ = transaction.addMessages(additionalMessages, location: .Random) - var peers: [Peer] = additionalPeers - var peerPresences: [PeerId: PeerPresence] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + updatePeers(transaction: transaction, peers: peers + additionalPeers, update: { _, updated -> Peer in return updated }) updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: peerPresences) @@ -574,16 +575,15 @@ func fetchChatListHole(postbox: Postbox, network: Network, accountPeerId: PeerId transaction.replaceChatListHole(groupId: groupId, index: hole.index, hole: nil) } } - return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages in - for peer in fetchedChats.peers { - updatePeers(transaction: transaction, peers: [peer], update: { _, updated -> Peer in - return updated - }) - } + return withResolvedAssociatedMessages(postbox: postbox, source: .network(network), peers: Dictionary(fetchedChats.peers.map({ ($0.id, $0) }), uniquingKeysWith: { lhs, _ in lhs }), storeMessages: fetchedChats.storeMessages, { transaction, additionalPeers, additionalMessages in + updatePeers(transaction: transaction, peers: fetchedChats.peers + additionalPeers, update: { _, updated -> Peer in + return updated + }) updatePeerPresences(transaction: transaction, accountPeerId: accountPeerId, peerPresences: fetchedChats.peerPresences) transaction.updateCurrentPeerNotificationSettings(fetchedChats.notificationSettings) let _ = transaction.addMessages(fetchedChats.storeMessages, location: .UpperHistoryBlock) + let _ = transaction.addMessages(additionalMessages, location: .Random) transaction.resetIncomingReadStates(fetchedChats.readStates) transaction.replaceChatListHole(groupId: groupId, index: hole.index, hole: fetchedChats.lowerNonPinnedIndex.flatMap(ChatListHole.init)) diff --git a/TelegramCore/PeerPhotoUpdater.swift b/TelegramCore/PeerPhotoUpdater.swift index d03470025c..a6a5038681 100644 --- a/TelegramCore/PeerPhotoUpdater.swift +++ b/TelegramCore/PeerPhotoUpdater.swift @@ -19,8 +19,8 @@ public enum UploadPeerPhotoError { case generic } -public func updateAccountPhoto(account: Account, resource: MediaResource?) -> Signal { - return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: account.peerId, photo: resource.flatMap({ uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) })) +public func updateAccountPhoto(account: Account, resource: MediaResource?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal { + return updatePeerPhoto(postbox: account.postbox, network: account.network, stateManager: account.stateManager, accountPeerId: account.peerId, peerId: account.peerId, photo: resource.flatMap({ uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: $0) }), mapResourceToAvatarSizes: mapResourceToAvatarSizes) } public struct UploadedPeerPhotoData { @@ -43,125 +43,138 @@ public func uploadedPeerPhoto(postbox: Postbox, network: Network, resource: Medi } } -public func updatePeerPhoto(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peerId: PeerId, photo: Signal?) -> Signal { - return updatePeerPhotoInternal(postbox: postbox, network: network, stateManager: stateManager, accountPeerId: accountPeerId, peer: postbox.loadedPeerWithId(peerId), photo: photo) +public func updatePeerPhoto(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peerId: PeerId, photo: Signal?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal { + return updatePeerPhotoInternal(postbox: postbox, network: network, stateManager: stateManager, accountPeerId: accountPeerId, peer: postbox.loadedPeerWithId(peerId), photo: photo, mapResourceToAvatarSizes: mapResourceToAvatarSizes) } -public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peer: Signal, photo: Signal?) -> Signal { +public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateManager: AccountStateManager?, accountPeerId: PeerId, peer: Signal, photo: Signal?, mapResourceToAvatarSizes: @escaping (MediaResource, [TelegramMediaImageRepresentation]) -> Signal<[Int: Data], NoError>) -> Signal { return peer |> mapError {_ in return .generic} |> mapToSignal { peer -> Signal in if let photo = photo { return photo - |> mapError { _ -> UploadPeerPhotoError in return .generic } - |> mapToSignal { result -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in - switch result.content { - case .error: - return .fail(.generic) - case let .result(resultData): - switch resultData { - case let .progress(progress): - return .single((.progress(progress), result.resource)) - case let .inputFile(file): - if peer is TelegramUser { - return network.request(Api.functions.photos.uploadProfilePhoto(file: file)) - |> mapError {_ in return UploadPeerPhotoError.generic} - |> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in - - let representations: [TelegramMediaImageRepresentation] - switch photo { - case let .photo(photo: apiPhoto, users: _): - switch apiPhoto { - case .photoEmpty: - representations = [] - case let .photo(photo): - var sizes = photo.sizes - if sizes.count == 3 { - sizes.remove(at: 1) - } - representations = telegramMediaImageRepresentationsFromApiSizes(sizes).1 - if let resource = result.resource as? LocalFileReferenceMediaResource { - if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { - for representation in representations { - postbox.mediaBox.storeResourceData(representation.resource.id, data: data) - } + |> mapError { _ -> UploadPeerPhotoError in return .generic } + |> mapToSignal { result -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in + switch result.content { + case .error: + return .fail(.generic) + case let .result(resultData): + switch resultData { + case let .progress(progress): + return .single((.progress(progress), result.resource)) + case let .inputFile(file): + if peer is TelegramUser { + return network.request(Api.functions.photos.uploadProfilePhoto(file: file)) + |> mapError {_ in return UploadPeerPhotoError.generic} + |> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in + + let representations: [TelegramMediaImageRepresentation] + switch photo { + case let .photo(photo: apiPhoto, users: _): + switch apiPhoto { + case .photoEmpty: + representations = [] + case let .photo(photo): + var sizes = photo.sizes + if sizes.count == 3 { + sizes.remove(at: 1) + } + representations = telegramMediaImageRepresentationsFromApiSizes(sizes).1 + if let resource = result.resource as? LocalFileReferenceMediaResource { + if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { + for representation in representations { + postbox.mediaBox.storeResourceData(representation.resource.id, data: data) } } - - } - } - return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in - if let peer = transaction.getPeer(peer.id) { - updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in - if let peer = peer as? TelegramUser { - return peer.withUpdatedPhoto(representations) - } else { - return peer - } - }) - } - return (.complete(representations), result.resource) + } - } |> mapError {_ in return UploadPeerPhotoError.generic} + } } + return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in + if let peer = transaction.getPeer(peer.id) { + updatePeers(transaction: transaction, peers: [peer], update: { (_, peer) -> Peer? in + if let peer = peer as? TelegramUser { + return peer.withUpdatedPhoto(representations) + } else { + return peer + } + }) + } + return (.complete(representations), result.resource) + + } |> mapError {_ in return UploadPeerPhotoError.generic} + } + } else { + let request: Signal + if let peer = peer as? TelegramGroup { + request = network.request(Api.functions.messages.editChatPhoto(chatId: peer.id.id, photo: .inputChatUploadedPhoto(file: file))) + } else if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { + request = network.request(Api.functions.channels.editPhoto(channel: inputChannel, photo: .inputChatUploadedPhoto(file: file))) } else { - let request: Signal - if let peer = peer as? TelegramGroup { - request = network.request(Api.functions.messages.editChatPhoto(chatId: peer.id.id, photo: .inputChatUploadedPhoto(file: file))) - } else if let peer = peer as? TelegramChannel, let inputChannel = apiInputChannel(peer) { - request = network.request(Api.functions.channels.editPhoto(channel: inputChannel, photo: .inputChatUploadedPhoto(file: file))) - } else { - assertionFailure() - request = .complete() + assertionFailure() + request = .complete() + } + + return request + |> mapError {_ in return UploadPeerPhotoError.generic} + |> mapToSignal { updates -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in + guard let chat = updates.chats.first, chat.peerId == peer.id, let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) else { + stateManager?.addUpdates(updates) + return .fail(.generic) } - return request |> mapError {_ in return UploadPeerPhotoError.generic} |> mapToSignal { updates -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in + return mapResourceToAvatarSizes(result.resource, groupOrChannel.profileImageRepresentations) + |> introduceError(UploadPeerPhotoError.self) + |> mapToSignal { generatedData -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in stateManager?.addUpdates(updates) - for chat in updates.chats { - if chat.peerId == peer.id { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in - updatePeers(transaction: transaction, peers: [groupOrChannel], update: { _, updated in - return updated - }) - return (.complete(groupOrChannel.profileImageRepresentations), result.resource) - } |> mapError { _ in return .generic } - } + + for (index, data) in generatedData { + if index >= 0 && index < groupOrChannel.profileImageRepresentations.count { + postbox.mediaBox.storeResourceData(groupOrChannel.profileImageRepresentations[index].resource.id, data: data) + } else { + assertionFailure() } } - return .fail(.generic) + return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in + updatePeers(transaction: transaction, peers: [groupOrChannel], update: { _, updated in + return updated + }) + return (.complete(groupOrChannel.profileImageRepresentations), result.resource) + } + |> mapError { _ in return .generic } } } - default: - return .fail(.generic) - } - } + } + default: + return .fail(.generic) + } } - |> map { result, resource -> UpdatePeerPhotoStatus in - switch result { - case let .complete(representations): - if let resource = resource as? LocalFileReferenceMediaResource { - if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { - for representation in representations { - postbox.mediaBox.storeResourceData(representation.resource.id, data: data) - } + } + |> map { result, resource -> UpdatePeerPhotoStatus in + switch result { + case let .complete(representations): + if let resource = resource as? LocalFileReferenceMediaResource { + if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { + for representation in representations { + postbox.mediaBox.storeResourceData(representation.resource.id, data: data) } } - default: - break - } - return result + } + default: + break } + return result + } } else { if let _ = peer as? TelegramUser { return network.request(Api.functions.photos.updateProfilePhoto(id: Api.InputPhoto.inputPhotoEmpty)) - |> `catch` { _ -> Signal in - return .fail(.generic) - } - |> mapToSignal { _ -> Signal in - return .single(.complete([])) - } + |> `catch` { _ -> Signal in + return .fail(.generic) + } + |> mapToSignal { _ -> Signal in + return .single(.complete([])) + } } else { let request: Signal if let peer = peer as? TelegramGroup { @@ -173,7 +186,9 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan request = .complete() } - return request |> mapError {_ in return UploadPeerPhotoError.generic} |> mapToSignal { updates -> Signal in + return request + |> mapError {_ in return UploadPeerPhotoError.generic} + |> mapToSignal { updates -> Signal in stateManager?.addUpdates(updates) for chat in updates.chats { if chat.peerId == peer.id { @@ -183,7 +198,8 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan return updated }) return .complete(groupOrChannel.profileImageRepresentations) - } |> mapError { _ in return .generic } + } + |> mapError { _ in return .generic } } } }