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

This commit is contained in:
overtake 2020-10-12 15:50:36 +03:00
commit 09aeedc9cb
26 changed files with 264 additions and 79 deletions

View File

@ -3,7 +3,7 @@
include Utils.makefile include Utils.makefile
APP_VERSION="7.1.1" APP_VERSION="7.1.2"
CORE_COUNT=$(shell sysctl -n hw.logicalcpu) CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1) CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)

View File

@ -569,7 +569,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
parsedAttributes.append(AutoremoveTimeoutMessageAttribute(timeout: autoremoveTimeout, countdownBeginTime: countdownBeginTime)) 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)) 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") //Logger.shared.log("loadLegacyMessages", "message \(messageId) completed")

View File

@ -98,8 +98,9 @@ public extension MessageTags {
static let gif = MessageTags(rawValue: 1 << 7) static let gif = MessageTags(rawValue: 1 << 7)
static let photo = MessageTags(rawValue: 1 << 8) static let photo = MessageTags(rawValue: 1 << 8)
static let video = MessageTags(rawValue: 1 << 9) 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 { public extension GlobalMessageTags {

View File

@ -26,7 +26,10 @@ public let telegramPostboxSeedConfiguration: SeedConfiguration = {
var upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [:] var upgradedMessageHoles: [PeerId.Namespace: [MessageId.Namespace: Set<MessageTags>]] = [:]
for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles { for peerNamespace in peerIdNamespacesWithInitialCloudMessageHoles {
upgradedMessageHoles[peerNamespace] = [ upgradedMessageHoles[peerNamespace] = [
Namespaces.Message.Cloud: Set(MessageTags.gif) Namespaces.Message.Cloud: Set([
MessageTags.gif,
MessageTags.pinned
])
] ]
} }

View File

@ -259,6 +259,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) } dict[1178116716] = { return Api.Update.parse_updateReadChannelDiscussionOutbox($0) }
dict[610945826] = { return Api.Update.parse_updatePeerBlocked($0) } dict[610945826] = { return Api.Update.parse_updatePeerBlocked($0) }
dict[-13975905] = { return Api.Update.parse_updateChannelUserTyping($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[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($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[470789295] = { return Api.ChannelParticipant.parse_channelParticipantBanned($0) }
dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) } dict[-859915345] = { return Api.ChannelParticipant.parse_channelParticipantAdmin($0) }
dict[1149094475] = { return Api.ChannelParticipant.parse_channelParticipantCreator($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[-1567730343] = { return Api.MessageUserVote.parse_messageUserVote($0) }
dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) } dict[909603888] = { return Api.MessageUserVote.parse_messageUserVoteInputOption($0) }
dict[244310238] = { return Api.MessageUserVote.parse_messageUserVoteMultiple($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[1187706024] = { return Api.MessagesFilter.parse_inputMessagesFilterMyMentionsUnread($0) }
dict[-419271411] = { return Api.MessagesFilter.parse_inputMessagesFilterGeo($0) } dict[-419271411] = { return Api.MessagesFilter.parse_inputMessagesFilterGeo($0) }
dict[-530392189] = { return Api.MessagesFilter.parse_inputMessagesFilterContacts($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[364538944] = { return Api.messages.Dialogs.parse_dialogs($0) }
dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) } dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) }
dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) } dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) }

View File

@ -6178,6 +6178,7 @@ public extension Api {
case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32) case updateReadChannelDiscussionOutbox(channelId: Int32, topMsgId: Int32, readMaxId: Int32)
case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool) case updatePeerBlocked(peerId: Api.Peer, blocked: Api.Bool)
case updateChannelUserTyping(flags: Int32, channelId: Int32, topMsgId: Int32?, userId: Int32, action: Api.SendMessageAction) 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) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -6907,6 +6908,20 @@ public extension Api {
serializeInt32(userId, buffer: buffer, boxed: false) serializeInt32(userId, buffer: buffer, boxed: false)
action.serialize(buffer, true) action.serialize(buffer, true)
break 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)]) return ("updatePeerBlocked", [("peerId", peerId), ("blocked", blocked)])
case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action): case .updateChannelUserTyping(let flags, let channelId, let topMsgId, let userId, let action):
return ("updateChannelUserTyping", [("flags", flags), ("channelId", channelId), ("topMsgId", topMsgId), ("userId", userId), ("action", 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 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 { 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 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 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 channelParticipantCreator(flags: Int32, userId: Int32, adminRights: Api.ChatAdminRights, rank: String?)
case channelParticipantLeft(userId: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -8684,6 +8727,12 @@ public extension Api {
adminRights.serialize(buffer, true) adminRights.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeString(rank!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeString(rank!, buffer: buffer, boxed: false)}
break 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)]) 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): case .channelParticipantCreator(let flags, let userId, let adminRights, let rank):
return ("channelParticipantCreator", [("flags", flags), ("userId", userId), ("adminRights", adminRights), ("rank", 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 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 { public enum MessageUserVote: TypeConstructorDescription {
@ -19590,6 +19652,7 @@ public extension Api {
case inputMessagesFilterMyMentionsUnread case inputMessagesFilterMyMentionsUnread
case inputMessagesFilterGeo case inputMessagesFilterGeo
case inputMessagesFilterContacts case inputMessagesFilterContacts
case inputMessagesFilterPinned
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
@ -19700,6 +19763,12 @@ public extension Api {
buffer.appendInt32(-530392189) buffer.appendInt32(-530392189)
} }
break
case .inputMessagesFilterPinned:
if boxed {
buffer.appendInt32(464520273)
}
break break
} }
} }
@ -19742,6 +19811,8 @@ public extension Api {
return ("inputMessagesFilterGeo", []) return ("inputMessagesFilterGeo", [])
case .inputMessagesFilterContacts: case .inputMessagesFilterContacts:
return ("inputMessagesFilterContacts", []) return ("inputMessagesFilterContacts", [])
case .inputMessagesFilterPinned:
return ("inputMessagesFilterPinned", [])
} }
} }
@ -19807,6 +19878,9 @@ public extension Api {
public static func parse_inputMessagesFilterContacts(_ reader: BufferReader) -> MessagesFilter? { public static func parse_inputMessagesFilterContacts(_ reader: BufferReader) -> MessagesFilter? {
return Api.MessagesFilter.inputMessagesFilterContacts return Api.MessagesFilter.inputMessagesFilterContacts
} }
public static func parse_inputMessagesFilterPinned(_ reader: BufferReader) -> MessagesFilter? {
return Api.MessagesFilter.inputMessagesFilterPinned
}
} }
public enum EmojiKeyword: TypeConstructorDescription { public enum EmojiKeyword: TypeConstructorDescription {

View File

@ -2963,22 +2963,6 @@ public extension Api {
}) })
} }
public static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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<Api.Updates>) { public static func sendVote(peer: Api.InputPeer, msgId: Int32, options: [Buffer]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(283795844) buffer.appendInt32(283795844)
@ -3780,6 +3764,22 @@ public extension Api {
return result return result
}) })
} }
public static func updatePinnedMessage(flags: Int32, peer: Api.InputPeer, id: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
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 struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) { public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {

View File

@ -85,6 +85,7 @@ enum AccountStateMutationOperation {
case UpdatePeer(PeerId, (Peer?) -> Peer?) case UpdatePeer(PeerId, (Peer?) -> Peer?)
case UpdateIsContact(PeerId, Bool) case UpdateIsContact(PeerId, Bool)
case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?) case UpdateCachedPeerData(PeerId, (CachedPeerData?) -> CachedPeerData?)
case UpdateMessagesPinned([MessageId], Bool)
case MergeApiUsers([Api.User]) case MergeApiUsers([Api.User])
case MergePeerPresences([PeerId: Api.UserStatus], Bool) case MergePeerPresences([PeerId: Api.UserStatus], Bool)
case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32) case UpdateSecretChat(chat: Api.EncryptedChat, timestamp: Int32)
@ -369,6 +370,10 @@ struct AccountMutableState {
self.addOperation(.UpdateCachedPeerData(id, f)) 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?) { mutating func updateLangPack(langCode: String, difference: Api.LangPackDifference?) {
self.addOperation(.UpdateLangPack(langCode, difference)) self.addOperation(.UpdateLangPack(langCode, difference))
} }
@ -479,7 +484,7 @@ struct AccountMutableState {
mutating func addOperation(_ operation: AccountStateMutationOperation) { mutating func addOperation(_ operation: AccountStateMutationOperation) {
switch operation { 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 break
case let .AddMessages(messages, location): case let .AddMessages(messages, location):
for message in messages { for message in messages {

View File

@ -663,6 +663,13 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
} else { } else {
updatesByChannel[peerId]!.append(update) 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, _, _): case let .updateNewChannelMessage(message, _, _):
if let peerId = apiMessagePeerId(message) { if let peerId = apiMessagePeerId(message) {
if updatesByChannel[peerId] == nil { if updatesByChannel[peerId] == nil {
@ -718,6 +725,8 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
lhsPts = pts lhsPts = pts
case let .updateEditChannelMessage(_, pts, _): case let .updateEditChannelMessage(_, pts, _):
lhsPts = pts lhsPts = pts
case let .updatePinnedChannelMessages(_, _, _, pts, _):
lhsPts = pts
default: default:
break break
} }
@ -731,6 +740,8 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
rhsPts = pts rhsPts = pts
case let .updateEditChannelMessage(_, pts, _): case let .updateEditChannelMessage(_, pts, _):
rhsPts = pts rhsPts = pts
case let .updatePinnedChannelMessages(_, channelId, _, pts, _):
rhsPts = pts
default: default:
break break
} }
@ -1106,7 +1117,9 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
}) })
case let .updateChannelPinnedMessage(channelId, id): case let .updateChannelPinnedMessage(channelId, id):
let channelPeerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId) 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 let previous: CachedChannelData
if let current = current as? CachedChannelData { if let current = current as? CachedChannelData {
previous = current previous = current
@ -1114,7 +1127,31 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
previous = CachedChannelData() previous = CachedChannelData()
} }
return previous.withUpdatedPinnedMessageId(id == 0 ? nil : MessageId(peerId: channelPeerId, namespace: Namespaces.Message.Cloud, id: id)) 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): case let .updateUserPinnedMessage(userId, id):
let userPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) let userPeerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
updatedState.updateCachedPeerData(userPeerId, { current in 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)) 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): case let .updateChannelReadMessagesContents(_, messages):
updatedState.addReadMessagesContents((peer.id, messages)) updatedState.addReadMessagesContents((peer.id, messages))
case let .updateChannelMessageViews(_, id, views): case let .updateChannelMessageViews(_, id, views):
@ -2107,7 +2149,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
var currentAddScheduledMessages: OptimizeAddMessagesState? var currentAddScheduledMessages: OptimizeAddMessagesState?
for operation in operations { for operation in operations {
switch operation { 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 { if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location)) 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 transaction.updatePeerCachedData(peerIds: Set([id]), update: { _, current in
return f(current) 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): case let .MergePeerPresences(statuses, explicit):
var presences: [PeerId: PeerPresence] = [:] var presences: [PeerId: PeerPresence] = [:]
for (peerId, status) in statuses { for (peerId, status) in statuses {

View File

@ -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) { if currentMessage.id.peerId.namespace == Namespaces.Peer.CloudChannel, !currentMessage.flags.contains(.Incoming), !Namespaces.Message.allScheduled.contains(currentMessage.id.namespace) {
let peerId = currentMessage.id.peerId 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)) 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))
}) })

View File

@ -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) 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): case let .channelParticipantSelf(userId, _, date):
self = .member(id: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), invitedAt: date, adminInfo: nil, banInfo: nil, rank: nil) 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)
} }
} }
} }

View File

@ -378,7 +378,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
authorId = account.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 = [] var localTags: LocalMessageTags = []
for media in mediaList { for media in mediaList {
@ -571,7 +571,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId,
attributes.removeAll(where: { $0 is OutgoingScheduleInfoMessageAttribute }) 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? let localGroupingKey: Int64?
switch grouping { switch grouping {

View File

@ -23,6 +23,8 @@ func messageFilterForTagMask(_ tagMask: MessageTags) -> Api.MessagesFilter? {
return Api.MessagesFilter.inputMessagesFilterRoundVoice return Api.MessagesFilter.inputMessagesFilterRoundVoice
} else if tagMask == .gif { } else if tagMask == .gif {
return Api.MessagesFilter.inputMessagesFilterGif return Api.MessagesFilter.inputMessagesFilterGif
} else if tagMask == .pinned {
return Api.MessagesFilter.inputMessagesFilterPinned
} else { } else {
return nil return nil
} }

View File

@ -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) 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): 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) 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): 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) 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): case let .decryptedMessageService(randomId, action):

View File

@ -4,7 +4,7 @@ import TelegramApi
import SyncCore 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 isSecret = false
var isUnconsumedPersonalMention = false var isUnconsumedPersonalMention = false
for attribute in attributes { for attribute in attributes {
@ -26,6 +26,10 @@ public func tagsForStoreMessage(incoming: Bool, attributes: [MessageAttribute],
tags.insert(.unseenPersonalMessage) tags.insert(.unseenPersonalMessage)
} }
if isPinned {
tags.insert(.pinned)
}
for attachment in media { for attachment in media {
if let _ = attachment as? TelegramMediaImage { if let _ = attachment as? TelegramMediaImage {
if !isSecret { if !isSecret {
@ -582,7 +586,9 @@ extension StoreMessage {
attributes.append(NotificationInfoMessageAttribute(flags: notificationFlags)) 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) storeFlags.insert(.CanBeGroupedIntoFeed)
@ -643,7 +649,7 @@ extension StoreMessage {
media.append(action) 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) storeFlags.insert(.CanBeGroupedIntoFeed)

View File

@ -15,7 +15,7 @@ func updateMessageMedia(transaction: Transaction, id: MediaId, media: Media?) {
break 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 { if tags == currentMessage.tags {
return .skip return .skip
} }

View File

@ -12,7 +12,7 @@ public enum UpdatePinnedMessageError {
public enum PinnedMessageUpdate { public enum PinnedMessageUpdate {
case pin(id: MessageId, silent: Bool) case pin(id: MessageId, silent: Bool)
case clear case clear(id: MessageId)
} }
public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: PinnedMessageUpdate) -> Signal<Void, UpdatePinnedMessageError> { public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update: PinnedMessageUpdate) -> Signal<Void, UpdatePinnedMessageError> {
@ -20,7 +20,6 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId)) return (transaction.getPeer(peerId), transaction.getPeerCachedData(peerId: peerId))
} }
|> mapError { _ -> UpdatePinnedMessageError in |> mapError { _ -> UpdatePinnedMessageError in
return .generic
} }
|> mapToSignal { peer, cachedPeerData -> Signal<Void, UpdatePinnedMessageError> in |> mapToSignal { peer, cachedPeerData -> Signal<Void, UpdatePinnedMessageError> in
guard let peer = peer, let inputPeer = apiInputPeer(peer) else { guard let peer = peer, let inputPeer = apiInputPeer(peer) else {
@ -38,8 +37,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
if silent { if silent {
flags |= (1 << 0) flags |= (1 << 0)
} }
case .clear: case let .clear(id):
messageId = 0 messageId = id.id
flags |= 1 << 1
} }
let request = Api.functions.messages.updatePinnedMessage(flags: flags, peer: inputPeer, id: messageId) 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<Void, UpdatePinnedMessageError> in |> mapToSignal { updates -> Signal<Void, UpdatePinnedMessageError> in
account.stateManager.addUpdates(updates) account.stateManager.addUpdates(updates)
return account.postbox.transaction { transaction in return account.postbox.transaction { _ in
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in /*transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in
if let current = current as? CachedChannelData { if let current = current as? CachedChannelData {
let pinnedMessageId: MessageId? let pinnedMessageId: MessageId?
switch update { switch update {
@ -64,9 +64,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
} else { } else {
return current return current
} }
}) })*/
} }
|> mapError { _ -> UpdatePinnedMessageError in return .generic |> mapError { _ -> UpdatePinnedMessageError in
} }
} }
} else { } else {
@ -97,8 +97,9 @@ public func requestUpdatePinnedMessage(account: Account, peerId: PeerId, update:
if silent { if silent {
flags |= (1 << 0) flags |= (1 << 0)
} }
case .clear: case let .clear(id):
messageId = 0 messageId = id.id
flags |= 1 << 1
} }
let request = Api.functions.messages.updatePinnedMessage(flags: flags, peer: inputPeer, id: messageId) 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 |> mapError { _ -> UpdatePinnedMessageError in
return .generic
} }
} }
} else { } else {

View File

@ -268,6 +268,8 @@ extension Api.Update {
} }
case let .updateDeleteChannelMessages(channelId, _, _, _): case let .updateDeleteChannelMessages(channelId, _, _, _):
return [PeerId(namespace: Namespaces.Peer.CloudChannel, id: 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, _, _): case let .updateNewChannelMessage(message, _, _):
return apiMessagePeerIds(message) return apiMessagePeerIds(message)
case let .updateEditChannelMessage(message, _, _): case let .updateEditChannelMessage(message, _, _):

View File

@ -3404,8 +3404,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let topPinnedMessage: Signal<ChatPinnedMessage?, NoError> let topPinnedMessage: Signal<ChatPinnedMessage?, NoError>
switch self.chatLocation { switch self.chatLocation {
case let .peer(peerId): case let .peer(peerId) where peerId.namespace == Namespaces.Peer.CloudChannel:
let replyHistory: Signal<ChatHistoryViewUpdate, NoError> = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.photoOrVideo, additionalData: []) let replyHistory: Signal<ChatHistoryViewUpdate, NoError> = (chatHistoryViewForLocation(ChatHistoryLocationInput(content: .Initial(count: 100), id: 0), context: self.context, chatLocation: .peer(peerId), chatLocationContextHolder: Atomic<ChatLocationContextHolder?>(value: nil), scheduled: false, fixedCombinedReadStates: nil, tagMask: MessageTags.pinned, additionalData: [])
|> castError(Bool.self) |> castError(Bool.self)
|> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in |> mapToSignal { update -> Signal<ChatHistoryViewUpdate, Bool> in
switch update { switch update {
@ -3424,9 +3424,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
topPinnedMessage = combineLatest( topPinnedMessage = combineLatest(
replyHistory, replyHistory,
self.chatDisplayNode.historyNode.topVisibleMessage.get() self.chatDisplayNode.historyNode.topVisibleMessageRange.get()
) )
|> map { update, topVisibleMessage -> ChatPinnedMessage? in |> map { update, topVisibleMessageRange -> ChatPinnedMessage? in
var message: ChatPinnedMessage? var message: ChatPinnedMessage?
switch update { switch update {
case .Loading: case .Loading:
@ -3437,8 +3437,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
var matches = false var matches = false
if message == nil { if message == nil {
matches = true matches = true
} else if let topVisibleMessage = topVisibleMessage { } else if let topVisibleMessageRange = topVisibleMessageRange {
if entry.message.id < topVisibleMessage.id { if entry.message.id < topVisibleMessageRange.lowerBound {
matches = true matches = true
} }
} else { } else {
@ -3453,7 +3453,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return message return message
} }
|> distinctUntilChanged |> distinctUntilChanged
case .replyThread: default:
topPinnedMessage = .single(nil) topPinnedMessage = .single(nil)
} }
@ -3486,7 +3486,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
var pinnedMessage: ChatPinnedMessage? var pinnedMessage: ChatPinnedMessage?
if case let .replyThread(replyThreadMessage) = strongSelf.chatLocation { switch strongSelf.chatLocation {
case let .replyThread(replyThreadMessage):
if isTopReplyThreadMessageShown { if isTopReplyThreadMessageShown {
pinnedMessageId = nil pinnedMessageId = nil
} else { } else {
@ -3497,14 +3498,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
pinnedMessage = ChatPinnedMessage(message: message, isLatest: true) pinnedMessage = ChatPinnedMessage(message: message, isLatest: true)
} }
} }
} else { case let .peer(peerId):
if let pinnedMessageId = pinnedMessageId { if peerId.namespace == Namespaces.Peer.CloudChannel {
if let message = messages?[pinnedMessageId] { pinnedMessageId = topPinnedMessage?.message.id
pinnedMessage = ChatPinnedMessage(message: message, isLatest: true) 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 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 strongSelf = self {
if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { if let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer {
var canManagePin = false var canManagePin = false
@ -4859,7 +4863,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
disposable = MetaDisposable() disposable = MetaDisposable()
strongSelf.unpinMessageDisposable = disposable 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)) })]), in: .window(.root))
} else { } else {

View File

@ -24,8 +24,9 @@ extension ChatReplyThreadMessage {
} }
} }
struct ChatTopVisibleMessage: Equatable { struct ChatTopVisibleMessageRange: Equatable {
var id: MessageId var lowerBound: MessageId
var upperBound: MessageId
var isLast: Bool var isLast: Bool
} }
@ -563,7 +564,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private var loadedMessagesFromCachedDataDisposable: Disposable? private var loadedMessagesFromCachedDataDisposable: Disposable?
let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true) let isTopReplyThreadMessageShown = ValuePromise<Bool>(false, ignoreRepeated: true)
let topVisibleMessage = ValuePromise<ChatTopVisibleMessage?>(nil, ignoreRepeated: true) let topVisibleMessageRange = ValuePromise<ChatTopVisibleMessageRange?>(nil, ignoreRepeated: true)
private let clientId: Atomic<Int32> private let clientId: Atomic<Int32>
@ -1161,7 +1162,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) { private func processDisplayedItemRangeChanged(displayedRange: ListViewDisplayedItemRange, transactionState: ChatHistoryTransactionOpaqueState) {
let historyView = transactionState.historyView let historyView = transactionState.historyView
var isTopReplyThreadMessageShownValue = false var isTopReplyThreadMessageShownValue = false
var topVisibleMessage: ChatTopVisibleMessage? var topVisibleMessageRange: ChatTopVisibleMessageRange?
if let visible = displayedRange.visibleRange { if let visible = displayedRange.visibleRange {
let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex) let indexRange = (historyView.filteredEntries.count - 1 - visible.lastIndex, historyView.filteredEntries.count - 1 - visible.firstIndex)
if indexRange.0 > indexRange.1 { 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 { if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
isTopReplyThreadMessageShownValue = true 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, _): case let .MessageGroupEntry(_, messages, _):
for (message, _, _, _) in messages { for (message, _, _, _) in messages {
var hasUnconsumedMention = false var hasUnconsumedMention = false
@ -1290,7 +1295,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id { if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
isTopReplyThreadMessageShownValue = true 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: default:
break break
@ -1410,7 +1419,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
} }
} }
self.isTopReplyThreadMessageShown.set(isTopReplyThreadMessageShownValue) 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 let loaded = displayedRange.loadedRange, let firstEntry = historyView.filteredEntries.first, let lastEntry = historyView.filteredEntries.last {
if loaded.firstIndex < 5 && historyView.originalView.laterId != nil { if loaded.firstIndex < 5 && historyView.originalView.laterId != nil {

View File

@ -643,7 +643,14 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
} }
if data.canPin, case .peer = chatPresentationInterfaceState.chatLocation { 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 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) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Pin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { _, f in
@ -654,7 +661,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_Unpin, icon: { theme in 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) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Unpin"), color: theme.actionSheet.primaryTextColor)
}, action: { _, f in }, action: { _, f in
interfaceInteraction.unpinMessage() interfaceInteraction.unpinMessage(messages[0].id)
f(.default) f(.default)
}))) })))
} }

View File

@ -95,7 +95,7 @@ final class ChatPanelInterfaceInteraction {
let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool let sendSticker: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
let unblockPeer: () -> Void let unblockPeer: () -> Void
let pinMessage: (MessageId) -> Void let pinMessage: (MessageId) -> Void
let unpinMessage: () -> Void let unpinMessage: (MessageId) -> Void
let shareAccountContact: () -> Void let shareAccountContact: () -> Void
let reportPeer: () -> Void let reportPeer: () -> Void
let presentPeerContact: () -> Void let presentPeerContact: () -> Void
@ -171,7 +171,7 @@ final class ChatPanelInterfaceInteraction {
sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool,
unblockPeer: @escaping () -> Void, unblockPeer: @escaping () -> Void,
pinMessage: @escaping (MessageId) -> Void, pinMessage: @escaping (MessageId) -> Void,
unpinMessage: @escaping () -> Void, unpinMessage: @escaping (MessageId) -> Void,
shareAccountContact: @escaping () -> Void, shareAccountContact: @escaping () -> Void,
reportPeer: @escaping () -> Void, reportPeer: @escaping () -> Void,
presentPeerContact: @escaping () -> Void, presentPeerContact: @escaping () -> Void,

View File

@ -98,7 +98,6 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
} }
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
self.addSubnode(self.closeButton)
self.addSubnode(self.clippingContainer) self.addSubnode(self.clippingContainer)
self.clippingContainer.addSubnode(self.contentContainer) self.clippingContainer.addSubnode(self.contentContainer)
@ -107,6 +106,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
self.contentContainer.addSubnode(self.textNode) self.contentContainer.addSubnode(self.textNode)
self.contentContainer.addSubnode(self.imageNode) self.contentContainer.addSubnode(self.imageNode)
self.addSubnode(self.closeButton)
self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside]) self.tapButton.addTarget(self, action: #selector(self.tapped), forControlEvents: [.touchUpInside])
self.addSubnode(self.tapButton) self.addSubnode(self.tapButton)
@ -360,6 +361,8 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
} }
@objc func closePressed() { @objc func closePressed() {
self.interfaceInteraction?.unpinMessage() if let interfaceInteraction = self.interfaceInteraction, let message = self.currentMessage {
interfaceInteraction.unpinMessage(message.message.id)
}
} }
} }

View File

@ -100,7 +100,7 @@ final class ChatRecentActionsController: TelegramBaseController {
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _ in }, pinMessage: { _ in
}, unpinMessage: { }, unpinMessage: { _ in
}, shareAccountContact: { }, shareAccountContact: {
}, reportPeer: { }, reportPeer: {
}, presentPeerContact: { }, presentPeerContact: {

View File

@ -405,7 +405,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
return false return false
}, unblockPeer: { }, unblockPeer: {
}, pinMessage: { _ in }, pinMessage: { _ in
}, unpinMessage: { }, unpinMessage: { _ in
}, shareAccountContact: { }, shareAccountContact: {
}, reportPeer: { }, reportPeer: {
}, presentPeerContact: { }, 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: { 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() interaction.editingOpenPreHistorySetup()
})) }))

View File

@ -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 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) callSessionManager?.sendSignalingData(internalId: internalId, data: data)
}, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec) }, videoCapturer: video?.impl, preferredVideoCodec: preferredVideoCodec, audioInputDeviceId: "")
strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context)) strongSelf.contextRef = Unmanaged.passRetained(OngoingCallThreadLocalContextHolder(context))
context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in context.stateChanged = { [weak callSessionManager] state, videoState, remoteVideoState, remoteAudioState, remoteBatteryLevel, _ in