diff --git a/Makefile b/Makefile index db4f5cdcf8..ce8502e8ae 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include Utils.makefile -APP_VERSION="7.1.1" +APP_VERSION="7.1.2" CORE_COUNT=$(shell sysctl -n hw.logicalcpu) CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1) diff --git a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift index 8a14aebe91..3280e88720 100644 --- a/submodules/LegacyDataImport/Sources/LegacyChatImport.swift +++ b/submodules/LegacyDataImport/Sources/LegacyChatImport.swift @@ -569,7 +569,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc parsedAttributes.append(AutoremoveTimeoutMessageAttribute(timeout: autoremoveTimeout, countdownBeginTime: countdownBeginTime)) } - let (parsedTags, parsedGlobalTags) = tagsForStoreMessage(incoming: parsedFlags.contains(.Incoming), attributes: parsedAttributes, media: parsedMedia, textEntities: nil) + let (parsedTags, parsedGlobalTags) = tagsForStoreMessage(incoming: parsedFlags.contains(.Incoming), attributes: parsedAttributes, media: parsedMedia, textEntities: nil, isPinned: false) messages.append(StoreMessage(id: parsedId, globallyUniqueId: globallyUniqueId, groupingKey: parsedGroupingKey, threadId: nil, timestamp: timestamp, flags: parsedFlags, tags: parsedTags, globalTags: parsedGlobalTags, localTags: [], forwardInfo: nil, authorId: parsedAuthorId, text: text, attributes: parsedAttributes, media: parsedMedia)) //Logger.shared.log("loadLegacyMessages", "message \(messageId) completed") diff --git a/submodules/SyncCore/Sources/Namespaces.swift b/submodules/SyncCore/Sources/Namespaces.swift index 6a7ddf90d5..0cc10c542a 100644 --- a/submodules/SyncCore/Sources/Namespaces.swift +++ b/submodules/SyncCore/Sources/Namespaces.swift @@ -98,8 +98,9 @@ public extension MessageTags { static let gif = MessageTags(rawValue: 1 << 7) static let photo = MessageTags(rawValue: 1 << 8) static let video = MessageTags(rawValue: 1 << 9) + static let pinned = MessageTags(rawValue: 1 << 10) - static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video] + static let all: MessageTags = [.photoOrVideo, .file, .music, .webPage, .voiceOrInstantVideo, .unseenPersonalMessage, .liveLocation, .gif, .photo, .video, .pinned] } public extension GlobalMessageTags { diff --git a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift index 6faaa9e537..508aedbefa 100644 --- a/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift +++ b/submodules/SyncCore/Sources/StandaloneAccountTransaction.swift @@ -26,7 +26,10 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = { var upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set]] = [:] for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles { upgradedMessageHoles[peerNamespace] = [ - Namespaces.Message.Cloud: Set(MessageTags.gif) + Namespaces.Message.Cloud: Set([ + MessageTags.gif, + MessageTags.pinned + ]) ] } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 874ebaa025..8613d5a417 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -259,6 +259,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) } dict[610945826] = { return Api.Update.parse_updatePeerBlocked($0) } dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($0) } + dict[-2054649973] = { return Api.Update.parse_updatePinnedChannelMessages($0) } dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) } @@ -267,6 +268,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) } dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) } dict[1149094475] = { return Api.ChannelParticipant.parse_channelParticipantCreator($0) } + dict[-1010402965] = { return Api.ChannelParticipant.parse_channelParticipantLeft($0) } dict[-1567730343] = { return Api.MessageUserVote.parse_messageUserVote($0) } dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) } dict[244310238] = { return Api.MessageUserVote.parse_messageUserVoteMultiple($0) } @@ -692,6 +694,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1187706024] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentionsUnread($0) } dict[-419271411] = { return Api.MessagesFilter.parse_inputMessagesFilterGeo($0) } dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($0) } + dict[464520273] = { return Api.MessagesFilter.parse_inputMessagesFilterPinned($0) } dict[364538944] = { return Api.messages.Dialogs.parse_dialogs($0) } dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) } dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) } diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index 1dd38eed77..f21d28d9ca 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -6178,6 +6178,7 @@ public extension Api { case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32) case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool) case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction) + case updatePinnedChannelMessages(flags: Int32, channelId: Int32, messages: [Int32], pts: Int32, ptsCount: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -6907,6 +6908,20 @@ public extension Api { serializeInt32(userId, buffer: buffer, boxed: false) action.serialize(buffer, true) break + case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): + if boxed { + buffer.appendInt32(-2054649973) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(channelId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(messages.count)) + for item in messages { + serializeInt32(item, buffer: buffer, boxed: false) + } + serializeInt32(pts, buffer: buffer, boxed: false) + serializeInt32(ptsCount, buffer: buffer, boxed: false) + break } } @@ -7084,6 +7099,8 @@ public extension Api { return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)]) case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action): return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("userId", userId), ("action", action)]) + case .updatePinnedChannelMessages(let flags, let channelId, let messages, let pts, let ptsCount): + return ("updatePinnedChannelMessages", [("flags", flags), ("channelId", channelId), ("messages", messages), ("pts", pts), ("ptsCount", ptsCount)]) } } @@ -8549,6 +8566,31 @@ public extension Api { return nil } } + public static func parse_updatePinnedChannelMessages(_ reader: BufferReader) -> Update? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: [Int32]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.Update.updatePinnedChannelMessages(flags: _1!, channelId: _2!, messages: _3!, pts: _4!, ptsCount: _5!) + } + else { + return nil + } + } } public enum PopularContact: TypeConstructorDescription { @@ -8635,6 +8677,7 @@ public extension Api { case channelParticipantBanned(flags: Int32, userId: Int32, kickedBy: Int32, date: Int32, bannedRights: Api.ChatBannedRights) case channelParticipantAdmin(flags: Int32, userId: Int32, inviterId: Int32?, promotedBy: Int32, date: Int32, adminRights: Api.ChatAdminRights, rank: String?) case channelParticipantCreator(flags: Int32, userId: Int32, adminRights: Api.ChatAdminRights, rank: String?) + case channelParticipantLeft(userId: Int32) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -8684,6 +8727,12 @@ public extension Api { adminRights.serialize(buffer, true) if Int(flags) & Int(1 << 0) != 0 {serializeString(rank!, buffer: buffer, boxed: false)} break + case .channelParticipantLeft(let userId): + if boxed { + buffer.appendInt32(-1010402965) + } + serializeInt32(userId, buffer: buffer, boxed: false) + break } } @@ -8699,6 +8748,8 @@ public extension Api { return ("channelParticipantAdmin", [("flags", flags), ("userId", userId), ("inviterId", inviterId), ("promotedBy", promotedBy), ("date", date), ("adminRights", adminRights), ("rank", rank)]) case .channelParticipantCreator(let flags, let userId, let adminRights, let rank): return ("channelParticipantCreator", [("flags", flags), ("userId", userId), ("adminRights", adminRights), ("rank", rank)]) + case .channelParticipantLeft(let userId): + return ("channelParticipantLeft", [("userId", userId)]) } } @@ -8811,6 +8862,17 @@ public extension Api { return nil } } + public static func parse_channelParticipantLeft(_ reader: BufferReader) -> ChannelParticipant? { + var _1: Int32? + _1 = reader.readInt32() + let _c1 = _1 != nil + if _c1 { + return Api.ChannelParticipant.channelParticipantLeft(userId: _1!) + } + else { + return nil + } + } } public enum MessageUserVote: TypeConstructorDescription { @@ -19590,6 +19652,7 @@ public extension Api { case inputMessagesFilterMyMentionsUnread case inputMessagesFilterGeo case inputMessagesFilterContacts + case inputMessagesFilterPinned public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -19700,6 +19763,12 @@ public extension Api { buffer.appendInt32(-530392189) } + break + case .inputMessagesFilterPinned: + if boxed { + buffer.appendInt32(464520273) + } + break } } @@ -19742,6 +19811,8 @@ public extension Api { return ("inputMessagesFilterGeo", []) case .inputMessagesFilterContacts: return ("inputMessagesFilterContacts", []) + case .inputMessagesFilterPinned: + return ("inputMessagesFilterPinned", []) } } @@ -19807,6 +19878,9 @@ public extension Api { public static func parse_inputMessagesFilterContacts(_ reader: BufferReader) -> MessagesFilter? { return Api.MessagesFilter.inputMessagesFilterContacts } + public static func parse_inputMessagesFilterPinned(_ reader: BufferReader) -> MessagesFilter? { + return Api.MessagesFilter.inputMessagesFilterPinned + } } public enum EmojiKeyword: TypeConstructorDescription { diff --git a/submodules/TelegramApi/Sources/Api3.swift b/submodules/TelegramApi/Sources/Api3.swift index e258ce144b..9dd2ea0f0f 100644 --- a/submodules/TelegramApi/Sources/Api3.swift +++ b/submodules/TelegramApi/Sources/Api3.swift @@ -2963,22 +2963,6 @@ public extension Api { }) } - public static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { - let buffer = Buffer() - buffer.appendInt32(-760547348) - serializeInt32(flags, buffer: buffer, boxed: false) - peer.serialize(buffer, true) - serializeInt32(id, buffer: buffer, boxed: false) - return (FunctionDescription(name: "messages.updatePinnedMessage", parameters: [("flags", flags), ("peer", peer), ("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in - let reader = BufferReader(buffer) - var result: Api.Updates? - if let signature = reader.readInt32() { - result = Api.parse(reader, signature: signature) as? Api.Updates - } - return result - }) - } - public static func sendVote(peer: Api.InputPeer, msgId: Int32, options: [Buffer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() buffer.appendInt32(283795844) @@ -3780,6 +3764,22 @@ public extension Api { return result }) } + + public static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-760547348) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(id, buffer: buffer, boxed: false) + return (FunctionDescription(name: "messages.updatePinnedMessage", parameters: [("flags", flags), ("peer", peer), ("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + let reader = BufferReader(buffer) + var result: Api.Updates? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Updates + } + return result + }) + } } public struct channels { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/submodules/TelegramCore/Sources/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/AccountIntermediateState.swift index 79698f31c8..0da4669378 100644 --- a/submodules/TelegramCore/Sources/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/AccountIntermediateState.swift @@ -85,6 +85,7 @@ enum AccountStateMutationOperation { case UpdatePeer(PeerId, (Peer?) -> Peer?) case UpdateIsContact(PeerId, Bool) case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?) + case UpdateMessagesPinned([MessageId], Bool) case MergeApiUsers([Api.User]) case MergePeerPresences([PeerId: Api.UserStatus], Bool) case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32) @@ -369,6 +370,10 @@ struct AccountMutableState { self.addOperation(.UpdateCachedPeerData(id, f)) } + mutating func updateMessagesPinned(ids: [MessageId], pinned: Bool) { + self.addOperation(.UpdateMessagesPinned(ids, pinned)) + } + mutating func updateLangPack(langCode: String, difference: Api.LangPackDifference?) { self.addOperation(.UpdateLangPack(langCode, difference)) } @@ -479,7 +484,7 @@ struct AccountMutableState { mutating func addOperation(_ operation: AccountStateMutationOperation) { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateMessagesPinned: break case let .AddMessages(messages, location): for message in messages { diff --git a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift index 0086c388b5..6973182383 100644 --- a/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/AccountStateManagementUtils.swift @@ -663,6 +663,13 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] { } else { updatesByChannel[peerId]!.append(update) } + case let .updatePinnedChannelMessages(_, channelId, _, _, _): + let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + if updatesByChannel[peerId] == nil { + updatesByChannel[peerId] = [update] + } else { + updatesByChannel[peerId]!.append(update) + } case let .updateNewChannelMessage(message, _, _): if let peerId = apiMessagePeerId(message) { if updatesByChannel[peerId] == nil { @@ -718,6 +725,8 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] { lhsPts = pts case let .updateEditChannelMessage(_, pts, _): lhsPts = pts + case let .updatePinnedChannelMessages(_, _, _, pts, _): + lhsPts = pts default: break } @@ -731,6 +740,8 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] { rhsPts = pts case let .updateEditChannelMessage(_, pts, _): rhsPts = pts + case let .updatePinnedChannelMessages(_, channelId, _, pts, _): + rhsPts = pts default: break } @@ -1106,7 +1117,9 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo }) case let .updateChannelPinnedMessage(channelId, id): let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) - updatedState.updateCachedPeerData(channelPeerId, { current in + updatedState.updateMessagesPinned(ids: [MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id)], pinned: true) + + /*updatedState.updateCachedPeerData(channelPeerId, { current in let previous: CachedChannelData if let current = current as? CachedChannelData { previous = current @@ -1114,7 +1127,31 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo previous = CachedChannelData() } return previous.withUpdatedPinnedMessageId(id == 0 ? nil : MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id)) - }) + })*/ + case let .updatePinnedChannelMessages(flags, channelId, messages, pts, ptsCount): + let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + if let previousState = updatedState.channelStates[peerId] { + if previousState.pts >= pts { + Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) skip old pinned messages update") + } else if previousState.pts + ptsCount == pts { + let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + updatedState.updateMessagesPinned(ids: messages.map { id in + MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id) + }, pinned: (flags & (1 << 0)) != 0) + updatedState.updateChannelState(peerId, pts: pts) + } else { + if !channelsToPoll.contains(peerId) { + Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) pinned messages pts hole") + channelsToPoll.insert(peerId) + //updatedMissingUpdates = true + } + } + } else { + if !channelsToPoll.contains(peerId) { + //Logger.shared.log("State", "channel \(peerId) (\((updatedState.peers[peerId] as? TelegramChannel)?.title ?? "nil")) state unknown") + channelsToPoll.insert(peerId) + } + } case let .updateUserPinnedMessage(userId, id): let userPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) updatedState.updateCachedPeerData(userPeerId, { current in @@ -1907,6 +1944,11 @@ private func pollChannel(network: Network, peer: Peer, state: AccountMutableStat } return previous.withUpdatedPinnedMessageId(id == 0 ? nil : MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id)) }) + case let .updatePinnedChannelMessages(flags, channelId, messages, _, _): + let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) + updatedState.updateMessagesPinned(ids: messages.map { id in + MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id) + }, pinned: (flags & (1 << 0)) != 0) case let .updateChannelReadMessagesContents(_, messages): updatedState.addReadMessagesContents((peer.id, messages)) case let .updateChannelMessageViews(_, id, views): @@ -2107,7 +2149,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation]) var currentAddScheduledMessages: OptimizeAddMessagesState? for operation in operations { switch operation { - case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread: + case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll/*, .UpdateMessageReactions*/, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned: if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty { result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) } @@ -2771,6 +2813,28 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP transaction.updatePeerCachedData(peerIds: Set([id]), update: { _, current in return f(current) }) + case let .UpdateMessagesPinned(messageIds, pinned): + for id in messageIds { + transaction.updateMessage(id, update: { currentMessage in + var storeForwardInfo: StoreMessageForwardInfo? + if let forwardInfo = currentMessage.forwardInfo { + storeForwardInfo = StoreMessageForwardInfo(forwardInfo) + } + + var tags = currentMessage.tags + if pinned { + tags.insert(.pinned) + } else { + tags.remove(.pinned) + } + + if tags == currentMessage.tags { + return .skip + } + + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: currentMessage.media)) + }) + } case let .MergePeerPresences(statuses, explicit): var presences: [PeerId: PeerPresence] = [:] for (peerId, status) in statuses { diff --git a/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift b/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift index 2ed805bb4b..483b116118 100644 --- a/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift +++ b/submodules/TelegramCore/Sources/ApplyUpdateMessage.swift @@ -198,7 +198,7 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes } } - let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities, isPinned: currentMessage.tags.contains(.pinned)) if currentMessage.id.peerId.namespace == Namespaces.Peer.CloudChannel, !currentMessage.flags.contains(.Incoming), !Namespaces.Message.allScheduled.contains(currentMessage.id.namespace) { let peerId = currentMessage.id.peerId @@ -374,7 +374,7 @@ func applyUpdateGroupMessages(postbox: Postbox, stateManager: AccountStateManage } } - let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: attributes, media: media, textEntities: entitiesAttribute?.entities, isPinned: currentMessage.tags.contains(.pinned)) return .update(StoreMessage(id: updatedId, globallyUniqueId: nil, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: updatedMessage.timestamp, flags: [], tags: tags, globalTags: globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: text, attributes: attributes, media: media)) }) diff --git a/submodules/TelegramCore/Sources/CachedChannelParticipants.swift b/submodules/TelegramCore/Sources/CachedChannelParticipants.swift index ea72e40a81..fc3d04d05f 100644 --- a/submodules/TelegramCore/Sources/CachedChannelParticipants.swift +++ b/submodules/TelegramCore/Sources/CachedChannelParticipants.swift @@ -210,6 +210,8 @@ extension ChannelParticipant { self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), invitedAt: date, adminInfo: ChannelParticipantAdminInfo(rights: TelegramChatAdminRights(apiAdminRights: adminRights), promotedBy: PeerId(namespace: Namespaces.Peer.CloudUser, id: promotedBy), canBeEditedByAccountPeer: (flags & (1 << 0)) != 0), banInfo: nil, rank: rank) case let .channelParticipantSelf(userId, _, date): self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil) + case let .channelParticipantLeft(userId): + self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), invitedAt: 0, adminInfo: nil, banInfo: nil, rank: nil) } } } diff --git a/submodules/TelegramCore/Sources/EnqueueMessage.swift b/submodules/TelegramCore/Sources/EnqueueMessage.swift index 56e46a2933..cfee4712d7 100644 --- a/submodules/TelegramCore/Sources/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/EnqueueMessage.swift @@ -378,7 +378,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, authorId = account.peerId } - let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: mediaList, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: mediaList, textEntities: entitiesAttribute?.entities, isPinned: false) var localTags: LocalMessageTags = [] for media in mediaList { @@ -571,7 +571,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, attributes.removeAll(where: { $0 is OutgoingScheduleInfoMessageAttribute }) } - let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: sourceMessage.media, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: false, attributes: attributes, media: sourceMessage.media, textEntities: entitiesAttribute?.entities, isPinned: false) let localGroupingKey: Int64? switch grouping { diff --git a/submodules/TelegramCore/Sources/Holes.swift b/submodules/TelegramCore/Sources/Holes.swift index 077236d559..f571e162e5 100644 --- a/submodules/TelegramCore/Sources/Holes.swift +++ b/submodules/TelegramCore/Sources/Holes.swift @@ -23,6 +23,8 @@ func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? { return Api.MessagesFilter.inputMessagesFilterRoundVoice } else if tagMask == .gif { return Api.MessagesFilter.inputMessagesFilterGif + } else if tagMask == .pinned { + return Api.MessagesFilter.inputMessagesFilterPinned } else { return nil } diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift index 431af1370b..8ff17a6ce9 100644 --- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift @@ -820,7 +820,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } } - let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: nil, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): @@ -1052,7 +1052,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } } - let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): @@ -1290,7 +1290,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } } - let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities) + let (tags, globalTags) = tagsForStoreMessage(incoming: true, attributes: attributes, media: parsedMedia, textEntities: entitiesAttribute?.entities, isPinned: false) return (StoreMessage(id: MessageId(peerId: peerId, namespace: Namespaces.Message.SecretIncoming, id: tagLocalIndex), globallyUniqueId: randomId, groupingKey: groupingKey, threadId: nil, timestamp: timestamp, flags: [.Incoming], tags: tags, globalTags: globalTags, localTags: [], forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: parsedMedia), resources) case let .decryptedMessageService(randomId, action): diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift index 7d5052d46e..a89d719e0e 100644 --- a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift @@ -4,7 +4,7 @@ import TelegramApi import SyncCore -public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], media: [Media], textEntities: [MessageTextEntity]?) -> (MessageTags, GlobalMessageTags) { +public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], media: [Media], textEntities: [MessageTextEntity]?, isPinned: Bool) -> (MessageTags, GlobalMessageTags) { var isSecret = false var isUnconsumedPersonalMention = false for attribute in attributes { @@ -26,6 +26,10 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute], tags.insert(.unseenPersonalMessage) } + if isPinned { + tags.insert(.pinned) + } + for attachment in media { if let _ = attachment as? TelegramMediaImage { if !isSecret { @@ -582,7 +586,9 @@ extension StoreMessage { attributes.append(NotificationInfoMessageAttribute(flags: notificationFlags)) } - let (tags, globalTags) = tagsForStoreMessage(incoming: storeFlags.contains(.Incoming), attributes: attributes, media: medias, textEntities: entitiesAttribute?.entities) + let isPinned = (flags & (1 << 24)) != 0 + + let (tags, globalTags) = tagsForStoreMessage(incoming: storeFlags.contains(.Incoming), attributes: attributes, media: medias, textEntities: entitiesAttribute?.entities, isPinned: isPinned) storeFlags.insert(.CanBeGroupedIntoFeed) @@ -643,7 +649,7 @@ extension StoreMessage { media.append(action) } - let (tags, globalTags) = tagsForStoreMessage(incoming: storeFlags.contains(.Incoming), attributes: attributes, media: media, textEntities: nil) + let (tags, globalTags) = tagsForStoreMessage(incoming: storeFlags.contains(.Incoming), attributes: attributes, media: media, textEntities: nil, isPinned: false) storeFlags.insert(.CanBeGroupedIntoFeed) diff --git a/submodules/TelegramCore/Sources/UpdateMessageMedia.swift b/submodules/TelegramCore/Sources/UpdateMessageMedia.swift index 764a2d8c57..2e477eedca 100644 --- a/submodules/TelegramCore/Sources/UpdateMessageMedia.swift +++ b/submodules/TelegramCore/Sources/UpdateMessageMedia.swift @@ -15,7 +15,7 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) { break } } - let (tags, _) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: currentMessage.attributes, media: currentMessage.media, textEntities: textEntities) + let (tags, _) = tagsForStoreMessage(incoming: currentMessage.flags.contains(.Incoming), attributes: currentMessage.attributes, media: currentMessage.media, textEntities: textEntities, isPinned: currentMessage.tags.contains(.pinned)) if tags == currentMessage.tags { return .skip } diff --git a/submodules/TelegramCore/Sources/UpdatePinnedMessage.swift b/submodules/TelegramCore/Sources/UpdatePinnedMessage.swift index 7e50ed5523..6bbf4f998d 100644 --- a/submodules/TelegramCore/Sources/UpdatePinnedMessage.swift +++ b/submodules/TelegramCore/Sources/UpdatePinnedMessage.swift @@ -12,7 +12,7 @@ public enum UpdatePinnedMessageError { public enum PinnedMessageUpdate { case pin(id: MessageId, silent: Bool) - case clear + case clear(id: MessageId) } public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: PinnedMessageUpdate) -> Signal { @@ -20,7 +20,6 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId)) } |> mapError { _ -> UpdatePinnedMessageError in - return .generic } |> mapToSignal { peer, cachedPeerData -> Signal in guard let peer = peer, let inputPeer = apiInputPeer(peer) else { @@ -38,8 +37,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: if silent { flags |= (1 << 0) } - case .clear: - messageId = 0 + case let .clear(id): + messageId = id.id + flags |= 1 << 1 } let request = Api.functions.messages.updatePinnedMessage(flags: flags, peer: inputPeer, id: messageId) @@ -50,8 +50,8 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: } |> mapToSignal { updates -> Signal in account.stateManager.addUpdates(updates) - return account.postbox.transaction { transaction in - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + return account.postbox.transaction { _ in + /*transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in if let current = current as? CachedChannelData { let pinnedMessageId: MessageId? switch update { @@ -64,9 +64,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: } else { return current } - }) + })*/ } - |> mapError { _ -> UpdatePinnedMessageError in return .generic + |> mapError { _ -> UpdatePinnedMessageError in } } } else { @@ -97,8 +97,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: if silent { flags |= (1 << 0) } - case .clear: - messageId = 0 + case let .clear(id): + messageId = id.id + flags |= 1 << 1 } let request = Api.functions.messages.updatePinnedMessage(flags: flags, peer: inputPeer, id: messageId) @@ -138,7 +139,6 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: }) } |> mapError { _ -> UpdatePinnedMessageError in - return .generic } } } else { diff --git a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift index 0b80c1de20..acf5d469a5 100644 --- a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift @@ -268,6 +268,8 @@ extension Api.Update { } case let .updateDeleteChannelMessages(channelId, _, _, _): return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)] + case let .updatePinnedChannelMessages(_, channelId, _, _, _): + return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)] case let .updateNewChannelMessage(message, _, _): return apiMessagePeerIds(message) case let .updateEditChannelMessage(message, _, _): diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index d1663b6355..b4e373c038 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3404,8 +3404,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let topPinnedMessage: Signal switch self.chatLocation { - case let .peer(peerId): - let replyHistory: Signal = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.photoOrVideo, additionalData: []) + case let .peer(peerId) where peerId.namespace == Namespaces.Peer.CloudChannel: + let replyHistory: Signal = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, additionalData: []) |> castError(Bool.self) |> mapToSignal { update -> Signal in switch update { @@ -3424,9 +3424,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G topPinnedMessage = combineLatest( replyHistory, - self.chatDisplayNode.historyNode.topVisibleMessage.get() + self.chatDisplayNode.historyNode.topVisibleMessageRange.get() ) - |> map { update, topVisibleMessage -> ChatPinnedMessage? in + |> map { update, topVisibleMessageRange -> ChatPinnedMessage? in var message: ChatPinnedMessage? switch update { case .Loading: @@ -3437,8 +3437,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var matches = false if message == nil { matches = true - } else if let topVisibleMessage = topVisibleMessage { - if entry.message.id < topVisibleMessage.id { + } else if let topVisibleMessageRange = topVisibleMessageRange { + if entry.message.id < topVisibleMessageRange.lowerBound { matches = true } } else { @@ -3453,7 +3453,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return message } |> distinctUntilChanged - case .replyThread: + default: topPinnedMessage = .single(nil) } @@ -3486,7 +3486,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var pinnedMessage: ChatPinnedMessage? - if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation { + switch strongSelf.chatLocation { + case let .replyThread(replyThreadMessage): if isTopReplyThreadMessageShown { pinnedMessageId = nil } else { @@ -3497,14 +3498,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G pinnedMessage = ChatPinnedMessage(message: message, isLatest: true) } } - } else { - if let pinnedMessageId = pinnedMessageId { - if let message = messages?[pinnedMessageId] { - pinnedMessage = ChatPinnedMessage(message: message, isLatest: true) + case let .peer(peerId): + if peerId.namespace == Namespaces.Peer.CloudChannel { + pinnedMessageId = topPinnedMessage?.message.id + pinnedMessage = topPinnedMessage + } else { + if let pinnedMessageId = pinnedMessageId { + if let message = messages?[pinnedMessageId] { + pinnedMessage = ChatPinnedMessage(message: message, isLatest: true) + } } } - //pinnedMessageId = topPinnedMessage?.message.id - //pinnedMessage = topPinnedMessage } var pinnedMessageUpdated = false @@ -4828,7 +4832,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } } - }, unpinMessage: { [weak self] in + }, unpinMessage: { [weak self] id in if let strongSelf = self { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { var canManagePin = false @@ -4859,7 +4863,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposable = MetaDisposable() strongSelf.unpinMessageDisposable = disposable } - disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear).start()) + disposable.set(requestUpdatePinnedMessage(account: strongSelf.context.account, peerId: peer.id, update: .clear(id: id)).start()) } })]), in: .window(.root)) } else { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 27d97c4b01..31f8d7b146 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -24,8 +24,9 @@ extension ChatReplyThreadMessage { } } -struct ChatTopVisibleMessage: Equatable { - var id: MessageId +struct ChatTopVisibleMessageRange: Equatable { + var lowerBound: MessageId + var upperBound: MessageId var isLast: Bool } @@ -563,7 +564,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private var loadedMessagesFromCachedDataDisposable: Disposable? let isTopReplyThreadMessageShown = ValuePromise(false, ignoreRepeated: true) - let topVisibleMessage = ValuePromise(nil, ignoreRepeated: true) + let topVisibleMessageRange = ValuePromise(nil, ignoreRepeated: true) private let clientId: Atomic @@ -1161,7 +1162,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) { let historyView = transactionState.historyView var isTopReplyThreadMessageShownValue = false - var topVisibleMessage: ChatTopVisibleMessage? + var topVisibleMessageRange: ChatTopVisibleMessageRange? if let visible = displayedRange.visibleRange { let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex) if indexRange.0 > indexRange.1 { @@ -1259,7 +1260,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id { isTopReplyThreadMessageShownValue = true } - topVisibleMessage = ChatTopVisibleMessage(id: message.id, isLast: i == historyView.filteredEntries.count - 1) + if let topVisibleMessageRangeValue = topVisibleMessageRange { + topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: topVisibleMessageRangeValue.lowerBound, upperBound: message.id, isLast: i == historyView.filteredEntries.count - 1) + } else { + topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: message.id, upperBound: message.id, isLast: i == historyView.filteredEntries.count - 1) + } case let .MessageGroupEntry(_, messages, _): for (message, _, _, _) in messages { var hasUnconsumedMention = false @@ -1290,7 +1295,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id { isTopReplyThreadMessageShownValue = true } - topVisibleMessage = ChatTopVisibleMessage(id: message.id, isLast: i == historyView.filteredEntries.count - 1) + if let topVisibleMessageRangeValue = topVisibleMessageRange { + topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: topVisibleMessageRangeValue.lowerBound, upperBound: message.id, isLast: i == historyView.filteredEntries.count - 1) + } else { + topVisibleMessageRange = ChatTopVisibleMessageRange(lowerBound: message.id, upperBound: message.id, isLast: i == historyView.filteredEntries.count - 1) + } } default: break @@ -1410,7 +1419,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } } self.isTopReplyThreadMessageShown.set(isTopReplyThreadMessageShownValue) - self.topVisibleMessage.set(topVisibleMessage) + self.topVisibleMessageRange.set(topVisibleMessageRange) if let loaded = displayedRange.loadedRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last { if loaded.firstIndex < 5 && historyView.originalView.laterId != nil { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 2c57eb9114..9d79b12b85 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -643,7 +643,14 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: } if data.canPin, case .peer = chatPresentationInterfaceState.chatLocation { - if chatPresentationInterfaceState.pinnedMessage?.message.id != messages[0].id { + let isPinned: Bool + if let _ = chatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel { + isPinned = messages[0].tags.contains(.pinned) + } else { + isPinned = chatPresentationInterfaceState.pinnedMessage?.message.id == messages[0].id + } + + if !isPinned { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Pin, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in @@ -654,7 +661,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState: actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor) }, action: { _, f in - interfaceInteraction.unpinMessage() + interfaceInteraction.unpinMessage(messages[0].id) f(.default) }))) } diff --git a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift index 0db13197a9..f0f1397f01 100644 --- a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift @@ -95,7 +95,7 @@ final class ChatPanelInterfaceInteraction { let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let unblockPeer: () -> Void let pinMessage: (MessageId) -> Void - let unpinMessage: () -> Void + let unpinMessage: (MessageId) -> Void let shareAccountContact: () -> Void let reportPeer: () -> Void let presentPeerContact: () -> Void @@ -171,7 +171,7 @@ final class ChatPanelInterfaceInteraction { sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, - unpinMessage: @escaping () -> Void, + unpinMessage: @escaping (MessageId) -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 298dc83e14..37c731f0df 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -98,7 +98,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) - self.addSubnode(self.closeButton) self.addSubnode(self.clippingContainer) self.clippingContainer.addSubnode(self.contentContainer) @@ -107,6 +106,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.contentContainer.addSubnode(self.textNode) self.contentContainer.addSubnode(self.imageNode) + self.addSubnode(self.closeButton) + self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside]) self.addSubnode(self.tapButton) @@ -360,6 +361,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } @objc func closePressed() { - self.interfaceInteraction?.unpinMessage() + if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage { + interfaceInteraction.unpinMessage(message.message.id) + } } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index 3ed5932fce..522c2b6b94 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -100,7 +100,7 @@ final class ChatRecentActionsController: TelegramBaseController { return false }, unblockPeer: { }, pinMessage: { _ in - }, unpinMessage: { + }, unpinMessage: { _ in }, shareAccountContact: { }, reportPeer: { }, presentPeerContact: { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index b3f23f2133..f3adab8351 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -405,7 +405,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { return false }, unblockPeer: { }, pinMessage: { _ in - }, unpinMessage: { + }, unpinMessage: { _ in }, shareAccountContact: { }, reportPeer: { }, presentPeerContact: { @@ -1250,7 +1250,7 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr })) } } - if !isPublic && cachedData.linkedDiscussionPeerId == nil { + if !isPublic, case .known(nil) = cachedData.linkedDiscussionPeerId { items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemPreHistory, label: .text(cachedData.flags.contains(.preHistoryEnabled) ? presentationData.strings.GroupInfo_GroupHistoryVisible : presentationData.strings.GroupInfo_GroupHistoryHidden), text: presentationData.strings.GroupInfo_GroupHistory, action: { interaction.editingOpenPreHistorySetup() })) diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index f1a6fa752c..4966897420 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -627,7 +627,7 @@ public final class OngoingCallContext { let context = OngoingCallThreadLocalContextWebrtc(version: version, queue: OngoingCallThreadLocalContextQueueImpl(queue: queue), proxy: voipProxyServer, networkType: ongoingNetworkTypeForTypeWebrtc(initialNetworkType), dataSaving: ongoingDataSavingForTypeWebrtc(dataSaving), derivedState: derivedState.data, key: key, isOutgoing: isOutgoing, connections: filteredConnections, maxLayer: maxLayer, allowP2P: allowP2P, allowTCP: enableTCP, enableStunMarking: enableStunMarking, logPath: tempLogPath, statsLogPath: tempStatsLogPath, sendSignalingData: { [weak callSessionManager] data in callSessionManager?.sendSignalingData(internalId: internalId, data: data) - }, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec) + }, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "") strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in