Merge commit '50c01586839b0113730b0aaa9a4011b954868da2'

This commit is contained in:
Peter 2018-01-23 22:58:14 +04:00
commit 3024d9c43a
28 changed files with 766 additions and 757 deletions

View File

@ -151,7 +151,6 @@
D033FEB61E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; };
D033FEB71E61F3F900644997 /* BlockedPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D033FEB51E61F3F900644997 /* BlockedPeers.swift */; };
D03B0CB91D62233400955575 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CB81D62233400955575 /* Either.swift */; };
D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBA1D62233C00955575 /* MergeLists.swift */; };
D03B0CBD1D62234300955575 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; };
D03B0CBF1D62234A00955575 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
D03B0CC11D62235000955575 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.swift */; };
@ -454,7 +453,6 @@
D0B8440D1DAB91CD005F29E1 /* ImageRepresentationsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */; };
D0B8440E1DAB91CD005F29E1 /* MessageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */; };
D0B8440F1DAB91CD005F29E1 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CB81D62233400955575 /* Either.swift */; };
D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBA1D62233C00955575 /* MergeLists.swift */; };
D0B844111DAB91CD005F29E1 /* Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBC1D62234300955575 /* Regex.swift */; };
D0B844121DAB91CD005F29E1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CBE1D62234A00955575 /* Log.swift */; };
D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = D03B0CC01D62235000955575 /* StringFormat.swift */; };
@ -556,6 +554,8 @@
D0E35A151DE4C6A200BC6096 /* OutgoingMessageWithChatContextResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */; };
D0E6521F1E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
D0E652201E3A364A004EEA91 /* UpdateAccountPeerName.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */; };
D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; };
D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */; };
D0F02CE51E9926C40065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; };
D0F02CE61E9926C50065DEE2 /* ManagedConfigurationUpdates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */; };
D0F3A89F1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */; };
@ -694,7 +694,6 @@
D033FEB21E61F3C000644997 /* ReportPeer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportPeer.swift; sourceTree = "<group>"; };
D033FEB51E61F3F900644997 /* BlockedPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockedPeers.swift; sourceTree = "<group>"; };
D03B0CB81D62233400955575 /* Either.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = "<group>"; };
D03B0CBA1D62233C00955575 /* MergeLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MergeLists.swift; sourceTree = "<group>"; };
D03B0CBC1D62234300955575 /* Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Regex.swift; sourceTree = "<group>"; };
D03B0CBE1D62234A00955575 /* Log.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
D03B0CC01D62235000955575 /* StringFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringFormat.swift; sourceTree = "<group>"; };
@ -930,6 +929,7 @@
D0E35A0F1DE49E1C00BC6096 /* OutgoingMessageWithChatContextResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingMessageWithChatContextResult.swift; sourceTree = "<group>"; };
D0E35A111DE4A25E00BC6096 /* OutgoingChatContextResultMessageAttribute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingChatContextResultMessageAttribute.swift; sourceTree = "<group>"; };
D0E6521E1E3A364A004EEA91 /* UpdateAccountPeerName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateAccountPeerName.swift; sourceTree = "<group>"; };
D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelAdminEventLogContext.swift; sourceTree = "<group>"; };
D0F02CE41E9926C40065DEE2 /* ManagedConfigurationUpdates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedConfigurationUpdates.swift; sourceTree = "<group>"; };
D0F3A89E1E82C65400B4C64C /* SynchronizeChatInputStateOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronizeChatInputStateOperation.swift; sourceTree = "<group>"; };
D0F3A8A11E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedSynchronizeChatInputStateOperations.swift; sourceTree = "<group>"; };
@ -1090,7 +1090,6 @@
D0DF0C901D81A857008AEB01 /* ImageRepresentationsUtils.swift */,
D0DF0C921D81AD09008AEB01 /* MessageUtils.swift */,
D03B0CB81D62233400955575 /* Either.swift */,
D03B0CBA1D62233C00955575 /* MergeLists.swift */,
D03B0CBC1D62234300955575 /* Regex.swift */,
D03B0CBE1D62234A00955575 /* Log.swift */,
D03B0CC01D62235000955575 /* StringFormat.swift */,
@ -1575,6 +1574,7 @@
C23BC3861E9BE3CA00D79F92 /* ImportContact.swift */,
C205FEA71EB3B75900455808 /* ExportMessageLink.swift */,
C230BEB51EE9A3760029586C /* ChannelAdminEventLogs.swift */,
D0E817482010E7E300B82BBB /* ChannelAdminEventLogContext.swift */,
D0A472B51F4CBE8B00E0EEDA /* LoadedPeer.swift */,
D0DA1D311F7043D50034E892 /* ManagedPendingPeerNotificationSettings.swift */,
D02395D51F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift */,
@ -1950,6 +1950,7 @@
D0561DE31E5737FC00E6B9E9 /* UpdatePeerInfo.swift in Sources */,
D0DF0C8A1D819C7E008AEB01 /* JoinChannel.swift in Sources */,
D04CAA5A1E83310D0047E51F /* MD5.swift in Sources */,
D0E817492010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */,
D05452071E7B5093006EEF19 /* LoadedStickerPack.swift in Sources */,
D01C7F041EFC1C49008305F1 /* DeviceContact.swift in Sources */,
D0F7AB2F1DCF507E009AD9A1 /* ReplyMarkupMessageAttribute.swift in Sources */,
@ -2040,7 +2041,6 @@
D0528E651E65C82400E2FEF5 /* UpdateContactName.swift in Sources */,
D03121021DA57E93006A2A60 /* TelegramPeerNotificationSettings.swift in Sources */,
D0C48F391E8138DF0075317D /* ArchivedStickerPacksInfo.swift in Sources */,
D03B0CBB1D62233C00955575 /* MergeLists.swift in Sources */,
C239BE971E62EE1E00C2C453 /* LoadMessagesIfNecessary.swift in Sources */,
D03B0CC11D62235000955575 /* StringFormat.swift in Sources */,
D0B85AC51F6B2B9400B8B5CE /* RecentlyUsedHashtags.swift in Sources */,
@ -2289,6 +2289,7 @@
D050F2621E4A5AE700988324 /* GlobalNotificationSettings.swift in Sources */,
D0DFD5E01FCDBCFD0039B3B1 /* CachedSentMediaReferences.swift in Sources */,
D0B418991D7E0580004562A4 /* TelegramMediaMap.swift in Sources */,
D0E8174A2010E7E300B82BBB /* ChannelAdminEventLogContext.swift in Sources */,
D0561DEB1E5754FA00E6B9E9 /* ChannelAdmins.swift in Sources */,
D0AD02E41FFFA14800C1DCFF /* PeerLiveLocationsContext.swift in Sources */,
D0613FCB1E60440600202CDB /* InvitationLinks.swift in Sources */,
@ -2324,7 +2325,6 @@
D033FEB41E61F3C000644997 /* ReportPeer.swift in Sources */,
D0FA8BAE1E1FD6E2001E855B /* MemoryBufferExtensions.swift in Sources */,
D0FA8BB41E201B02001E855B /* ProcessSecretChatIncomingEncryptedOperations.swift in Sources */,
D0B844101DAB91CD005F29E1 /* MergeLists.swift in Sources */,
D0F3A8A31E82C65E00B4C64C /* ManagedSynchronizeChatInputStateOperations.swift in Sources */,
D0448CA61E29215A005A61A7 /* MediaResourceApiUtils.swift in Sources */,
D001F3F11E128A1C007A8C60 /* SynchronizePeerReadState.swift in Sources */,

View File

@ -802,13 +802,10 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
if !entities.isEmpty {
attributes.append(TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities)))
}
var messageText = text
let messageText = text
var medias: [Media] = []
let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let mediaText = mediaText {
messageText = mediaText
}
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let mediaValue = mediaValue {
medias.append(mediaValue)
}
@ -829,7 +826,7 @@ private func finalStateWithUpdates(account: Account, state: AccountMutableState,
updatedState.readInbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: maxId))
case let .updateReadHistoryOutbox(peer, maxId, _, _):
updatedState.readOutbox(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: maxId))
case let .updateReadFeed(feedId, maxPosition):
case let .updateReadFeed(_, feedId, maxPosition, unreadCount, unreadMutedCount):
switch maxPosition {
case let .feedPosition(date, peer, id):
let index = MessageIndex(id: MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: id), timestamp: date)
@ -1157,6 +1154,8 @@ private func resolveAssociatedMessages(account: Account, state: AccountMutableSt
return (messages, chats, users)
case let .channelMessages(_, _, _, messages, chats, users):
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
}
} |> `catch` { _ in
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))
@ -1318,7 +1317,14 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
}
}
return account.network.request(Api.functions.messages.getPeerDialogs(peers: inputPeers))
|> retryRequest
|> map(Optional.init)
|> `catch` { error -> Signal<Api.messages.PeerDialogs?, Void> in
if error.errorDescription == "CHANNEL_PRIVATE" && inputPeers.count == 1 {
return .single(nil)
} else {
return .single(nil)
}
}
|> map { result -> AccountMutableState in
var updatedState = state
@ -1331,74 +1337,76 @@ private func resetChannels(_ account: Account, peers: [Peer], state: AccountMuta
var channelStates: [PeerId: ChannelState] = [:]
var notificationSettings: [PeerId: PeerNotificationSettings] = [:]
switch result {
case let .peerDialogs(dialogs, messages, chats, users, _):
dialogsChats.append(contentsOf: chats)
dialogsUsers.append(contentsOf: users)
loop: for dialog in dialogs {
let apiPeer: Api.Peer
let apiReadInboxMaxId: Int32
let apiReadOutboxMaxId: Int32
let apiTopMessage: Int32
let apiUnreadCount: Int32
let apiUnreadMentionsCount: Int32
var apiChannelPts: Int32?
let apiNotificationSettings: Api.PeerNotifySettings
switch dialog {
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
apiPeer = peer
apiTopMessage = topMessage
apiReadInboxMaxId = readInboxMaxId
apiReadOutboxMaxId = readOutboxMaxId
apiUnreadCount = unreadCount
apiUnreadMentionsCount = unreadMentionsCount
apiNotificationSettings = peerNotificationSettings
apiChannelPts = pts
case .dialogFeed:
assertionFailure()
continue loop
}
if let result = result {
switch result {
case let .peerDialogs(dialogs, messages, chats, users, _):
dialogsChats.append(contentsOf: chats)
dialogsUsers.append(contentsOf: users)
let peerId: PeerId
switch apiPeer {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
if readStates[peerId] == nil {
readStates[peerId] = [:]
}
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
if apiTopMessage != 0 {
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
}
if let apiChannelPts = apiChannelPts {
channelStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts)
}
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
}
for message in messages {
if let storeMessage = StoreMessage(apiMessage: message) {
var updatedStoreMessage = storeMessage
if case let .Id(id) = storeMessage.id {
if let channelState = channelStates[id.peerId] {
var updatedAttributes = storeMessage.attributes
updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts))
updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes)
}
loop: for dialog in dialogs {
let apiPeer: Api.Peer
let apiReadInboxMaxId: Int32
let apiReadOutboxMaxId: Int32
let apiTopMessage: Int32
let apiUnreadCount: Int32
let apiUnreadMentionsCount: Int32
var apiChannelPts: Int32?
let apiNotificationSettings: Api.PeerNotifySettings
switch dialog {
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
apiPeer = peer
apiTopMessage = topMessage
apiReadInboxMaxId = readInboxMaxId
apiReadOutboxMaxId = readOutboxMaxId
apiUnreadCount = unreadCount
apiUnreadMentionsCount = unreadMentionsCount
apiNotificationSettings = peerNotificationSettings
apiChannelPts = pts
case .dialogFeed:
assertionFailure()
continue loop
}
storeMessages.append(updatedStoreMessage)
let peerId: PeerId
switch apiPeer {
case let .peerUser(userId):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
case let .peerChat(chatId):
peerId = PeerId(namespace: Namespaces.Peer.CloudGroup, id: chatId)
case let .peerChannel(channelId):
peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
}
if readStates[peerId] == nil {
readStates[peerId] = [:]
}
readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: apiTopMessage, count: apiUnreadCount)
if apiTopMessage != 0 {
mentionTagSummaries[peerId] = MessageHistoryTagNamespaceSummary(version: 1, count: apiUnreadMentionsCount, range: MessageHistoryTagNamespaceCountValidityRange(maxId: apiTopMessage))
}
if let apiChannelPts = apiChannelPts {
channelStates[peerId] = ChannelState(pts: apiChannelPts, invalidatedPts: apiChannelPts)
}
notificationSettings[peerId] = TelegramPeerNotificationSettings(apiSettings: apiNotificationSettings)
}
}
for message in messages {
if let storeMessage = StoreMessage(apiMessage: message) {
var updatedStoreMessage = storeMessage
if case let .Id(id) = storeMessage.id {
if let channelState = channelStates[id.peerId] {
var updatedAttributes = storeMessage.attributes
updatedAttributes.append(ChannelMessageStateVersionAttribute(pts: channelState.pts))
updatedStoreMessage = updatedStoreMessage.withUpdatedAttributes(updatedAttributes)
}
}
storeMessages.append(updatedStoreMessage)
}
}
}
}
updatedState.mergeChats(dialogsChats)
@ -1774,10 +1782,16 @@ func replayFinalState(accountPeerId: PeerId, mediaBox: MediaBox, modifier: Modif
modifier.deleteMessagesInRange(peerId: id.peerId, namespace: id.namespace, minId: 1, maxId: id.id)
case let .EditMessage(id, message):
modifier.updateMessage(id, update: { previousMessage in
var updatedFlags = message.flags
var updatedLocalTags = message.localTags
if previousMessage.localTags.contains(.OutgoingLiveLocation) {
updatedLocalTags.insert(.OutgoingLiveLocation)
}
if message.flags.contains(.Incoming) {
updatedFlags.insert(.Incoming)
} else {
updatedFlags.remove(.Incoming)
}
return .update(message.withUpdatedLocalTags(updatedLocalTags))
})
case let .UpdateMedia(id, media):

View File

@ -75,8 +75,8 @@ public final class AccountStateManager {
return self.isUpdatingValue.get()
}
private let notificationMessagesPipe = ValuePipe<[Message]>()
public var notificationMessages: Signal<[Message], NoError> {
private let notificationMessagesPipe = ValuePipe<[(Message, PeerGroupId?)]>()
public var notificationMessages: Signal<[(Message, PeerGroupId?)], NoError> {
return self.notificationMessagesPipe.signal()
}
@ -460,12 +460,12 @@ public final class AccountStateManager {
}
}
let signal = self.account.postbox.modify { modifier -> [Message] in
var messages: [Message] = []
let signal = self.account.postbox.modify { modifier -> [(Message, PeerGroupId?)] in
var messages: [(Message, PeerGroupId?)] = []
for id in events.addedIncomingMessageIds {
let (message, notify, _, _) = messageForNotification(modifier: modifier, id: id, alwaysReturnMessage: false)
if let message = message, notify {
messages.append(message)
messages.append((message, modifier.getPeerGroupId(message.id.peerId)))
}
}
return messages
@ -473,7 +473,7 @@ public final class AccountStateManager {
let _ = (signal |> deliverOn(self.queue)).start(next: { [weak self] messages in
if let strongSelf = self {
for message in messages {
for (message, _) in messages {
Logger.shared.log("State" , "notify: \(String(describing: messageMainPeer(message)?.displayTitle)): \(message.id)")
}

View File

@ -77,6 +77,10 @@ private func fetchWebpage(account: Account, messageId: MessageId) -> Signal<Void
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
messages = []
chats = []
users = []
}
return account.postbox.modify { modifier -> Void in

View File

@ -108,7 +108,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
case .chat, .chatEmpty, .chatForbidden, .channelForbidden:
return parseTelegramGroupOrChannel(chat: rhs)
case let .channel(flags, _, accessHash, title, username, photo, date, version, restrictionReason, adminRights, bannedRights, _, feedId):
if let _ = accessHash {
if accessHash != nil && (flags & (1 << 12)) == 0 {
return parseTelegramGroupOrChannel(chat: rhs)
} else if let lhs = lhs as? TelegramChannel {
var channelFlags = lhs.flags
@ -128,7 +128,7 @@ func mergeGroupOrChannel(lhs: Peer?, rhs: Api.Chat) -> Peer? {
}
info = .group(TelegramChannelGroupInfo(flags: infoFlags))
}
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, peerGroupId: feedId.flatMap { PeerGroupId(rawValue: $0) })
return TelegramChannel(id: lhs.id, accessHash: lhs.accessHash, title: title, username: username, photo: imageRepresentationsForApiChatPhoto(photo), creationDate: lhs.creationDate, version: lhs.version, participationStatus: lhs.participationStatus, info: info, flags: channelFlags, restrictionInfo: lhs.restrictionInfo, adminRights: lhs.adminRights, bannedRights: lhs.bannedRights, peerGroupId: lhs.peerGroupId)
} else {
return nil
}

View File

@ -50,6 +50,8 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
}
}
let channelPts = result.channelPts
var sentStickers: [TelegramMediaFile] = []
var sentGifs: [TelegramMediaFile] = []
@ -62,14 +64,14 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
}
let media: [Media]
let attributes: [MessageAttribute]
var attributes: [MessageAttribute]
let text: String
if let apiMessage = apiMessage, let updatedMessage = StoreMessage(apiMessage: apiMessage) {
media = updatedMessage.media
attributes = updatedMessage.attributes
text = updatedMessage.text
} else if case let .updateShortSentMessage(_, _, _, _, _, apiMedia, entities) = result {
let (_, mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId)
let (mediaValue, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId)
if let mediaValue = mediaValue {
media = [mediaValue]
} else {
@ -95,6 +97,16 @@ func applyUpdateMessage(postbox: Postbox, stateManager: AccountStateManager, mes
text = currentMessage.text
}
if let channelPts = channelPts {
for i in 0 ..< attributes.count {
if let _ = attributes[i] as? ChannelMessageStateVersionAttribute {
attributes.remove(at: i)
break
}
}
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
}
var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo {
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature)

View File

@ -0,0 +1,124 @@
#if os(macOS)
import PostboxMac
import SwiftSignalKitMac
#else
import Postbox
import SwiftSignalKit
#endif
public struct ChannelAdminEventLogEntry: Comparable {
public let event: AdminLogEvent
public let peers: [PeerId: Peer]
public static func ==(lhs: ChannelAdminEventLogEntry, rhs: ChannelAdminEventLogEntry) -> Bool {
return lhs.event == rhs.event
}
public static func <(lhs: ChannelAdminEventLogEntry, rhs: ChannelAdminEventLogEntry) -> Bool {
return lhs.event < rhs.event
}
}
public enum ChannelAdminEventLogUpdateType {
case initial
case generic
case load
}
public final class ChannelAdminEventLogContext {
private let queue: Queue = Queue.mainQueue()
private let postbox: Postbox
private let network: Network
private let peerId: PeerId
private var entries: [ChannelAdminEventLogEntry] = []
private var hasEarlier: Bool = true
private var loadingMoreEarlier: Bool = false
private var subscribers = Bag<([ChannelAdminEventLogEntry], Bool, ChannelAdminEventLogUpdateType) -> Void>()
private let loadMoreDisposable = MetaDisposable()
public init(postbox: Postbox, network: Network, peerId: PeerId) {
self.postbox = postbox
self.network = network
self.peerId = peerId
}
deinit {
self.loadMoreDisposable.dispose()
}
public func get() -> Signal<([ChannelAdminEventLogEntry], Bool, ChannelAdminEventLogUpdateType), NoError> {
let queue = self.queue
return Signal { [weak self] subscriber in
if let strongSelf = self {
subscriber.putNext((strongSelf.entries, strongSelf.hasEarlier, .initial))
let index = strongSelf.subscribers.add({ entries, hasEarlier, type in
subscriber.putNext((strongSelf.entries, strongSelf.hasEarlier, type))
})
return ActionDisposable {
queue.async {
if let strongSelf = self {
strongSelf.subscribers.remove(index)
}
}
}
} else {
return EmptyDisposable
}
} |> runOn(queue)
}
public func loadMoreEntries() {
assert(self.queue.isCurrent())
if self.loadingMoreEarlier {
return
}
let maxId: AdminLogEventId
if let last = self.entries.last {
maxId = last.event.id
} else {
maxId = AdminLogEventId.max
}
self.loadingMoreEarlier = true
self.loadMoreDisposable.set((channelAdminLogEvents(postbox: self.postbox, network: self.network, peerId: self.peerId, maxId: maxId, minId: AdminLogEventId.min, limit: 10, query: nil, filter: nil, admins: nil)
|> deliverOn(self.queue)).start(next: { [weak self] result in
if let strongSelf = self {
var events = result.events.sorted()
if let first = strongSelf.entries.first {
var clipIndex = events.count
for i in (0 ..< events.count).reversed() {
if events[i] >= first.event {
clipIndex = i - 1
}
}
if clipIndex < events.count {
events.removeSubrange(clipIndex ..< events.count)
}
}
strongSelf.hasEarlier = !events.isEmpty
var entries: [ChannelAdminEventLogEntry] = events.map { event in
return ChannelAdminEventLogEntry(event: event, peers: result.peers)
}
entries.append(contentsOf: strongSelf.entries)
strongSelf.entries = entries
strongSelf.loadingMoreEarlier = false
for subscriber in strongSelf.subscribers.copyItems() {
subscriber(strongSelf.entries, strongSelf.hasEarlier, .load)
}
}
}))
}
}

View File

@ -8,17 +8,29 @@
public typealias AdminLogEventId = Int64
public struct AdminLogEvent {
public struct AdminLogEvent: Comparable {
public let id: AdminLogEventId
public let peerId:PeerId
public let date:Int32
public let peerId: PeerId
public let date: Int32
public let action: AdminLogEventAction
public static func ==(lhs: AdminLogEvent, rhs: AdminLogEvent) -> Bool {
return lhs.id == rhs.id
}
public static func <(lhs: AdminLogEvent, rhs: AdminLogEvent) -> Bool {
if lhs.date != rhs.date {
return lhs.date < rhs.date
} else {
return lhs.id < rhs.id
}
}
}
public struct AdminLogEventsResult {
public let peerId: PeerId
public let peers:[PeerId: Peer]
public let events:[AdminLogEvent]
public let peers: [PeerId: Peer]
public let events: [AdminLogEvent]
}
public enum AdminLogEventAction {
@ -29,7 +41,7 @@ public enum AdminLogEventAction {
case toggleInvites(Bool)
case toggleSignatures(Bool)
case updatePinned(Message?)
case editMessage(prev: Message, new:Message)
case editMessage(prev: Message, new: Message)
case deleteMessage(Message)
case participantJoin
case participantLeave
@ -69,129 +81,129 @@ public struct AdminLogEventsFlags : OptionSet {
public static let editMessages = AdminLogEventsFlags(rawValue: 1 << 12)
public static let deleteMessages = AdminLogEventsFlags(rawValue: 1 << 13)
public static var all:[AdminLogEventsFlags] {
public static var all: [AdminLogEventsFlags] {
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .pinnedMessages, .editMessages, .deleteMessages]
}
public static var flags:AdminLogEventsFlags {
public static var flags: AdminLogEventsFlags {
return [.join, .leave, .invite, .ban, .unban, .kick, .unkick, .promote, .demote, .info, .settings, .pinnedMessages, .editMessages, .deleteMessages]
}
}
private func boolFromApiValue(_ value: Api.Bool) -> Bool {
switch value {
case .boolFalse:
return false
case .boolTrue:
return true
case .boolFalse:
return false
case .boolTrue:
return true
}
}
public func channelAdminLogEvents(_ account:Account, peerId:PeerId, maxId:AdminLogEventId, minId:AdminLogEventId, limit:Int32 = 100, query:String? = nil, filter:AdminLogEventsFlags? = nil, admins:[PeerId]? = nil) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> {
return account.postbox.modify { modifier -> (Peer?, [Peer]?) in
return (modifier.getPeer(peerId), admins?.flatMap {modifier.getPeer($0)})
} |> mapError {return .generic} |> mapToSignal { (peer, admins) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> in
public func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, maxId: AdminLogEventId, minId: AdminLogEventId, limit: Int32 = 100, query: String? = nil, filter: AdminLogEventsFlags? = nil, admins: [PeerId]? = nil) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> {
return postbox.modify { modifier -> (Peer?, [Peer]?) in
return (modifier.getPeer(peerId), admins?.flatMap { modifier.getPeer($0) })
}
|> mapError { return .generic }
|> mapToSignal { (peer, admins) -> Signal<AdminLogEventsResult, ChannelAdminLogEventError> in
if let peer = peer, let inputChannel = apiInputChannel(peer) {
let inputAdmins = admins?.flatMap {apiInputUser($0)}
if let peer = peer, let inputChannel = apiInputChannel(peer) {
let inputAdmins = admins?.flatMap {apiInputUser($0)}
var flags:Int32 = 0
var eventsFilter:Api.ChannelAdminLogEventsFilter? = nil
if let filter = filter {
flags += Int32(1 << 0)
eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue))
}
if let _ = inputAdmins {
flags += Int32(1 << 1)
}
return account.network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) |> map { result in
switch result {
case let .adminLogResults(apiEvents, apiChats, apiUsers):
let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap { TelegramUser(user: $0) } + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in
var current = current
current[peer.id] = peer
return current
})
var events: [AdminLogEvent] = []
for event in apiEvents {
switch event {
case let .channelAdminLogEvent(id, date, userId, apiAction):
var action: AdminLogEventAction?
switch apiAction {
case let .channelAdminLogEventActionChangeTitle(prev, new):
action = .changeTitle(prev: prev, new: new)
case let .channelAdminLogEventActionChangeAbout(prev, new):
action = .changeAbout(prev: prev, new: new)
case let .channelAdminLogEventActionChangeUsername(prev, new):
action = .changeUsername(prev: prev, new: new)
case let .channelAdminLogEventActionChangePhoto(prev, new):
action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new))
case let .channelAdminLogEventActionToggleInvites(new):
action = .toggleInvites(boolFromApiValue(new))
case let .channelAdminLogEventActionToggleSignatures(new):
action = .toggleSignatures(boolFromApiValue(new))
case let .channelAdminLogEventActionUpdatePinned(new):
switch new {
case .messageEmpty:
action = .updatePinned(nil)
default:
if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) {
action = .updatePinned(rendered)
}
}
case let .channelAdminLogEventActionEditMessage(prev, new):
if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) {
action = .editMessage(prev: prevRendered, new: newRendered)
}
case let .channelAdminLogEventActionDeleteMessage(message):
if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) {
action = .deleteMessage(rendered)
}
case .channelAdminLogEventActionParticipantJoin:
action = .participantJoin
case .channelAdminLogEventActionParticipantLeave:
action = .participantLeave
case let .channelAdminLogEventActionParticipantInvite(participant):
let participant = ChannelParticipant(apiParticipant: participant)
if let peer = peers[participant.peerId] {
action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer))
}
case let .channelAdminLogEventActionParticipantToggleBan(prev, new):
let prevParticipant = ChannelParticipant(apiParticipant: prev)
let newParticipant = ChannelParticipant(apiParticipant: new)
if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] {
action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer))
}
case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new):
let prevParticipant = ChannelParticipant(apiParticipant: prev)
let newParticipant = ChannelParticipant(apiParticipant: new)
if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] {
action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer))
}
case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset):
action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset))
case let .channelAdminLogEventActionTogglePreHistoryHidden(value):
action = .togglePreHistoryHidden(value == .boolTrue)
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
if let action = action {
events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action))
}
}
}
return AdminLogEventsResult(peerId: peerId, peers: peers, events: events)
}
} |> mapError {_ in return .generic}
var flags: Int32 = 0
var eventsFilter: Api.ChannelAdminLogEventsFilter? = nil
if let filter = filter {
flags += Int32(1 << 0)
eventsFilter = Api.ChannelAdminLogEventsFilter.channelAdminLogEventsFilter(flags: Int32(filter.rawValue))
}
return .complete()
if let _ = inputAdmins {
flags += Int32(1 << 1)
}
return network.request(Api.functions.channels.getAdminLog(flags: flags, channel: inputChannel, q: query ?? "", eventsFilter: eventsFilter, admins: inputAdmins, maxId: maxId, minId: minId, limit: limit)) |> map { result in
switch result {
case let .adminLogResults(apiEvents, apiChats, apiUsers):
let peers = (apiChats.flatMap {parseTelegramGroupOrChannel(chat: $0)} + apiUsers.flatMap { TelegramUser(user: $0) } + Array(arrayLiteral: peer)).reduce([:], { current, peer -> [PeerId : Peer] in
var current = current
current[peer.id] = peer
return current
})
var events: [AdminLogEvent] = []
for event in apiEvents {
switch event {
case let .channelAdminLogEvent(id, date, userId, apiAction):
var action: AdminLogEventAction?
switch apiAction {
case let .channelAdminLogEventActionChangeTitle(prev, new):
action = .changeTitle(prev: prev, new: new)
case let .channelAdminLogEventActionChangeAbout(prev, new):
action = .changeAbout(prev: prev, new: new)
case let .channelAdminLogEventActionChangeUsername(prev, new):
action = .changeUsername(prev: prev, new: new)
case let .channelAdminLogEventActionChangePhoto(prev, new):
action = .changePhoto(prev: imageRepresentationsForApiChatPhoto(prev), new: imageRepresentationsForApiChatPhoto(new))
case let .channelAdminLogEventActionToggleInvites(new):
action = .toggleInvites(boolFromApiValue(new))
case let .channelAdminLogEventActionToggleSignatures(new):
action = .toggleSignatures(boolFromApiValue(new))
case let .channelAdminLogEventActionUpdatePinned(new):
switch new {
case .messageEmpty:
action = .updatePinned(nil)
default:
if let message = StoreMessage(apiMessage: new), let rendered = locallyRenderedMessage(message: message, peers: peers) {
action = .updatePinned(rendered)
}
}
case let .channelAdminLogEventActionEditMessage(prev, new):
if let prev = StoreMessage(apiMessage: prev), let prevRendered = locallyRenderedMessage(message: prev, peers: peers), let new = StoreMessage(apiMessage: new), let newRendered = locallyRenderedMessage(message: new, peers: peers) {
action = .editMessage(prev: prevRendered, new: newRendered)
}
case let .channelAdminLogEventActionDeleteMessage(message):
if let message = StoreMessage(apiMessage: message), let rendered = locallyRenderedMessage(message: message, peers: peers) {
action = .deleteMessage(rendered)
}
case .channelAdminLogEventActionParticipantJoin:
action = .participantJoin
case .channelAdminLogEventActionParticipantLeave:
action = .participantLeave
case let .channelAdminLogEventActionParticipantInvite(participant):
let participant = ChannelParticipant(apiParticipant: participant)
if let peer = peers[participant.peerId] {
action = .participantInvite(RenderedChannelParticipant(participant: participant, peer: peer))
}
case let .channelAdminLogEventActionParticipantToggleBan(prev, new):
let prevParticipant = ChannelParticipant(apiParticipant: prev)
let newParticipant = ChannelParticipant(apiParticipant: new)
if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] {
action = .participantToggleBan(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer))
}
case let .channelAdminLogEventActionParticipantToggleAdmin(prev, new):
let prevParticipant = ChannelParticipant(apiParticipant: prev)
let newParticipant = ChannelParticipant(apiParticipant: new)
if let prevPeer = peers[prevParticipant.peerId], let newPeer = peers[newParticipant.peerId] {
action = .participantToggleAdmin(prev: RenderedChannelParticipant(participant: prevParticipant, peer: prevPeer), new: RenderedChannelParticipant(participant: newParticipant, peer: newPeer))
}
case let .channelAdminLogEventActionChangeStickerSet(prevStickerset, newStickerset):
action = .changeStickerPack(prev: StickerPackReference(apiInputSet: prevStickerset), new: StickerPackReference(apiInputSet: newStickerset))
case let .channelAdminLogEventActionTogglePreHistoryHidden(value):
action = .togglePreHistoryHidden(value == .boolTrue)
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
if let action = action {
events.append(AdminLogEvent(id: id, peerId: peerId, date: date, action: action))
}
}
}
return AdminLogEventsResult(peerId: peerId, peers: peers, events: events)
}
} |> mapError {_ in return .generic}
}
return .complete()
}
}

View File

@ -10,7 +10,7 @@ import Foundation
#endif
public enum ChatContextResultMessage: PostboxCoding, Equatable {
case auto(caption: String, replyMarkup: ReplyMarkupMessageAttribute?)
case auto(caption: String, entities: TextEntitiesMessageAttribute?, replyMarkup: ReplyMarkupMessageAttribute?)
case text(text: String, entities: TextEntitiesMessageAttribute?, disableUrlPreview: Bool, replyMarkup: ReplyMarkupMessageAttribute?)
case mapLocation(media: TelegramMediaMap, replyMarkup: ReplyMarkupMessageAttribute?)
case contact(media: TelegramMediaContact, replyMarkup: ReplyMarkupMessageAttribute?)
@ -18,7 +18,7 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
public init(decoder: PostboxDecoder) {
switch decoder.decodeInt32ForKey("_v", orElse: 0) {
case 0:
self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
self = .auto(caption: decoder.decodeStringForKey("c", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 1:
self = .text(text: decoder.decodeStringForKey("t", orElse: ""), entities: decoder.decodeObjectForKey("e") as? TextEntitiesMessageAttribute, disableUrlPreview: decoder.decodeInt32ForKey("du", orElse: 0) != 0, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
case 2:
@ -26,15 +26,20 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
case 3:
self = .contact(media: decoder.decodeObjectForKey("c") as! TelegramMediaContact, replyMarkup: decoder.decodeObjectForKey("m") as? ReplyMarkupMessageAttribute)
default:
self = .auto(caption: "", replyMarkup: nil)
self = .auto(caption: "", entities: nil, replyMarkup: nil)
}
}
public func encode(_ encoder: PostboxEncoder) {
switch self {
case let .auto(caption, replyMarkup):
case let .auto(caption, entities, replyMarkup):
encoder.encodeInt32(0, forKey: "_v")
encoder.encodeString(caption, forKey: "c")
if let entities = entities {
encoder.encodeObject(entities, forKey: "e")
} else {
encoder.encodeNil(forKey: "e")
}
if let replyMarkup = replyMarkup {
encoder.encodeObject(replyMarkup, forKey: "m")
} else {
@ -75,11 +80,14 @@ public enum ChatContextResultMessage: PostboxCoding, Equatable {
public static func ==(lhs: ChatContextResultMessage, rhs: ChatContextResultMessage) -> Bool {
switch lhs {
case let .auto(lhsCaption, lhsReplyMarkup):
if case let .auto(rhsCaption, rhsReplyMarkup) = rhs {
case let .auto(lhsCaption, lhsEntities, lhsReplyMarkup):
if case let .auto(rhsCaption, rhsEntities, rhsReplyMarkup) = rhs {
if lhsCaption != rhsCaption {
return false
}
if lhsEntities != rhsEntities {
return false
}
if lhsReplyMarkup != rhsReplyMarkup {
return false
}
@ -324,12 +332,16 @@ public final class ChatContextResultCollection: Equatable {
extension ChatContextResultMessage {
init(apiMessage: Api.BotInlineMessage) {
switch apiMessage {
case let .botInlineMessageMediaAuto(_, caption, replyMarkup):
case let .botInlineMessageMediaAuto(_, message, entities, replyMarkup):
var parsedEntities: TextEntitiesMessageAttribute?
if let entities = entities, !entities.isEmpty {
parsedEntities = TextEntitiesMessageAttribute(entities: messageTextEntitiesFromApiEntities(entities))
}
var parsedReplyMarkup: ReplyMarkupMessageAttribute?
if let replyMarkup = replyMarkup {
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
}
self = .auto(caption: caption, replyMarkup: parsedReplyMarkup)
self = .auto(caption: message, entities: parsedEntities, replyMarkup: parsedReplyMarkup)
case let .botInlineMessageText(flags, message, entities, replyMarkup):
var parsedEntities: TextEntitiesMessageAttribute?
if let entities = entities, !entities.isEmpty {

View File

@ -61,7 +61,7 @@ final class HistoryViewChannelStateValidationContexts {
if ranges.isEmpty {
ranges = [[id]]
} else {
ranges[rangesToInvalidate.count - 1].append(id)
ranges[ranges.count - 1].append(id)
}
}
@ -122,6 +122,8 @@ final class HistoryViewChannelStateValidationContexts {
var addedRanges: [[MessageId]] = []
for messages in rangesToInvalidate {
for id in messages {
invalidatedMessageIds.insert(id)
if context.batchReferences[id] != nil {
addRangeBreak(&addedRanges)
} else {
@ -130,7 +132,7 @@ final class HistoryViewChannelStateValidationContexts {
}
}
if !addedRanges.isEmpty && addedRanges[rangesToInvalidate.count - 1].isEmpty {
if !addedRanges.isEmpty && addedRanges[addedRanges.count - 1].isEmpty {
addedRanges.removeLast()
}
@ -141,7 +143,7 @@ final class HistoryViewChannelStateValidationContexts {
context.batchReferences[messageId] = batch
}
disposable.set((validateBatch(postbox: self.postbox, network: self.network, messageIds: messages)
disposable.set((validateBatch(postbox: self.postbox, network: self.network, messageIds: messages, validatePts: invalidatedPts)
|> deliverOn(self.queue)).start(completed: { [weak self, weak batch] in
if let strongSelf = self, let context = strongSelf.contexts[id], let batch = batch {
var completedMessageIds: [MessageId] = []
@ -215,8 +217,10 @@ final class HistoryViewChannelStateValidationContexts {
private func hashForMessages(_ messages: [Message]) -> Int32 {
var acc: UInt32 = 0
for message in messages {
acc = (acc &* 20261) &+ message.id.id
let sorted = messages.sorted(by: { $0.id > $1.id })
for message in sorted {
acc = (acc &* 20261) &+ UInt32(message.id.id)
var timestamp = message.timestamp
inner: for attribute in message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
@ -224,25 +228,173 @@ private func hashForMessages(_ messages: [Message]) -> Int32 {
break inner
}
}
acc = (acc &* 20261) &+ timestamp
acc = (acc &* 20261) &+ UInt32(timestamp)
}
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
}
private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId]) -> Signal<Void, NoError> {
private func hashForMessages(_ messages: [StoreMessage]) -> Int32 {
var acc: UInt32 = 0
for message in messages {
if case let .Id(id) = message.id {
acc = (acc &* 20261) &+ UInt32(id.id)
var timestamp = message.timestamp
inner: for attribute in message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
timestamp = attribute.date
break inner
}
}
acc = (acc &* 20261) &+ UInt32(timestamp)
}
}
return Int32(bitPattern: acc & UInt32(0x7FFFFFFF))
}
private func validateBatch(postbox: Postbox, network: Network, messageIds: [MessageId], validatePts: Int32) -> Signal<Void, NoError> {
guard let peerId = messageIds.first?.peerId else {
return .never()
}
return postbox.modify { modifier -> Signal<Void, NoError> in
if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
var messages: [Message] = []
var previous: [MessageId: Message] = [:]
for messageId in messageIds {
if let message = modifier.getMessage(messageId) {
messages.append(message)
previous[message.id] = message
}
}
let hash = hashForMessages(messages)
return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id, offsetDate: 0, addOffset: 0, limit: 100, maxId: messageIds[messageIds.cout - 1].id, minId: messageIds[0].id))
return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: messageIds[messageIds.count - 1].id + 1, offsetDate: 0, addOffset: 0, limit: Int32(messageIds.count), maxId: messageIds[messageIds.count - 1].id + 1, minId: messageIds[0].id - 1, hash: hash))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.Messages?, NoError> in
return .single(nil)
}
|> mapToSignal { result -> Signal<Void, NoError> in
return postbox.modify { modifier -> Void in
if let result = result {
let messages: [Api.Message]
let chats: [Api.Chat]
let users: [Api.User]
var channelPts: Int32?
switch result {
case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
channelPts = pts
case .messagesNotModified:
for id in previous.keys {
modifier.updateMessage(id, update: { currentMessage in
var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo {
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature)
}
var attributes = currentMessage.attributes
/*if let channelPts = channelPts {
for i in 0 ..< attributes.count {
if let _ = attributes[i] as? ChannelMessageStateVersionAttribute {
attributes.remove(at: i)
break
}
}
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
}*/
for i in 0 ..< attributes.count {
if let _ = attributes[i] as? ChannelMessageStateVersionAttribute {
attributes.remove(at: i)
break
}
}
attributes.append(ChannelMessageStateVersionAttribute(pts: validatePts))
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
})
}
return
}
var storeMessages: [StoreMessage] = []
for message in messages {
if let storeMessage = StoreMessage(apiMessage: message) {
if let channelPts = channelPts {
var attributes = storeMessage.attributes
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
storeMessages.append(storeMessage.withUpdatedAttributes(attributes))
} else {
storeMessages.append(storeMessage)
}
}
}
//let updatedHash = hashForMessages(storeMessages)
var validMessageIds = Set<MessageId>()
for message in storeMessages {
if case let .Id(id) = message.id {
validMessageIds.insert(id)
if let previousMessage = previous[id] {
var updatedTimestamp = message.timestamp
inner: for attribute in message.attributes {
if let attribute = attribute as? EditedMessageAttribute {
updatedTimestamp = attribute.date
break inner
}
}
var timestamp = previousMessage.timestamp
inner: for attribute in previousMessage.attributes {
if let attribute = attribute as? EditedMessageAttribute {
timestamp = attribute.date
break inner
}
}
modifier.updateMessage(id, update: { currentMessage in
if updatedTimestamp != timestamp {
return .update(message)
} else {
var storeForwardInfo: StoreMessageForwardInfo?
if let forwardInfo = currentMessage.forwardInfo {
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature)
}
var attributes = currentMessage.attributes
if let channelPts = channelPts {
for i in 0 ..< attributes.count {
if let _ = attributes[i] as? ChannelMessageStateVersionAttribute {
attributes.remove(at: i)
break
}
}
attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts))
}
return .update(StoreMessage(id: message.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media))
}
})
}
}
}
for id in previous.keys {
if !validMessageIds.contains(id) {
modifier.deleteMessages([id])
}
}
}
}
}
} else {
return .never()
}

View File

@ -88,7 +88,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
default:
assertionFailure()
}
request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit)))
request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0))
} else if let filter = messageFilterForTagMask(tagMask) {
let offsetId: Int32
let addOffset: Int32
@ -119,7 +119,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
minId = 1
}
request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
} else {
assertionFailure()
request = .never()
@ -145,7 +145,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
if hole.maxIndex.timestamp == Int32.max {
let innerOffsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1)
let innerMaxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1)
maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1))
maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1, hash: 0))
|> map(Optional.init)
}
case let .AroundId(id):
@ -160,7 +160,7 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
minId = 1
}
request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId))
request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0))
}
return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest)
@ -183,6 +183,10 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
chats = apiChats
users = apiUsers
channelPts = pts
case .messagesNotModified:
messages = []
chats = []
users = []
}
var updatedMaxIndex: MessageIndex?
if let maxIndexResult = maxIndexResult {
@ -194,6 +198,8 @@ func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Pos
maxIndexMessages = apiMessages
case let .channelMessages(_, _, _, apiMessages, _, _):
maxIndexMessages = apiMessages
case .messagesNotModified:
maxIndexMessages = []
}
if !maxIndexMessages.isEmpty {
assert(maxIndexMessages.count == 1)
@ -501,7 +507,7 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd
offset = single((holeIndex.timestamp, min(holeIndex.id.id, Int32.max - 1) + 1, Api.InputPeer.inputPeerEmpty), NoError.self)
return offset
|> mapToSignal { (timestamp, id, peer) -> Signal<Void, NoError> in
let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0))
let searchResult = network.request(Api.functions.messages.search(flags: 0, peer: .inputPeerEmpty, q: "", fromId: nil, filter: .inputMessagesFilterPhoneCalls(flags: 0), minDate: 0, maxDate: holeIndex.timestamp, offsetId: 0, addOffset: 0, limit: limit, maxId: holeIndex.id.id, minId: 0, hash: 0))
|> retryRequest
|> mapToSignal { result -> Signal<Void, NoError> in
let messages: [Api.Message]
@ -520,6 +526,10 @@ func fetchCallListHole(network: Network, postbox: Postbox, holeIndex: MessageInd
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
messages = []
chats = []
users = []
}
return postbox.modify { modifier -> Void in
var storeMessages: [StoreMessage] = []

View File

@ -60,12 +60,14 @@ public func getMessagesLoadIfNecessary(_ messageIds:[MessageId], postbox:Postbox
if let signal = signal {
signals.append(signal |> map { result in
switch result {
case let .messages(messages, chats, users):
return (messages, chats, users)
case let .messagesSlice(_, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, messages, chats, users):
return (messages, chats, users)
case let .messages(messages, chats, users):
return (messages, chats, users)
case let .messagesSlice(_, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, messages, chats, users):
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
}
} |> `catch` { _ in
return Signal<([Api.Message], [Api.Chat], [Api.User]), NoError>.single(([], [], []))

View File

@ -43,6 +43,8 @@ public func loadedPeerFromMessage(account: Account, peerId: PeerId, messageId: M
apiUsers = users
case let .channelMessages(_, _, _, _, _, users):
apiUsers = users
case .messagesNotModified:
apiUsers = []
}
for user in apiUsers {

View File

@ -119,8 +119,8 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
switch item {
case let .peer(peerId):
return peerId.namespace != Namespaces.Peer.SecretChat
case .group:
return false
default:
return true
}
}
let localItemIds = modifier.getPinnedItemIds()
@ -128,17 +128,14 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
switch item {
case let .peer(peerId):
return peerId.namespace != Namespaces.Peer.SecretChat
case .group:
return false
default:
return true
}
}
return network.request(Api.functions.messages.getPinnedDialogs())
|> retryRequest
|> mapToSignal { dialogs -> Signal<Void, NoError> in
let dialogsChats: [Api.Chat]
let dialogsUsers: [Api.User]
var storeMessages: [StoreMessage] = []
var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:]
var chatStates: [PeerId: PeerChatState] = [:]
@ -146,10 +143,27 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
var remoteItemIds: [PinnedItemId] = []
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
switch dialogs {
case let .peerDialogs(dialogs, messages, chats, users, _):
dialogsChats = chats
dialogsUsers = users
var channelGroupIds: [PeerId: PeerGroupId] = [:]
for chat in chats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel)
if let channel = groupOrChannel as? TelegramChannel, let peerGroupId = channel.peerGroupId {
channelGroupIds[channel.id] = peerGroupId
}
}
}
for user in users {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
loop: for dialog in dialogs {
let apiPeer: Api.Peer
@ -161,6 +175,9 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
let apiNotificationSettings: Api.PeerNotifySettings
switch dialog {
case let .dialog(_, peer, topMessage, readInboxMaxId, readOutboxMaxId, unreadCount, unreadMentionsCount, peerNotificationSettings, pts, _):
if channelGroupIds[peer.peerId] != nil {
continue loop
}
apiPeer = peer
apiTopMessage = topMessage
apiReadInboxMaxId = readInboxMaxId
@ -204,21 +221,6 @@ private func synchronizePinnedChats(modifier: Modifier, postbox: Postbox, networ
}
}
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for chat in dialogsChats {
if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) {
peers.append(groupOrChannel)
}
}
for user in dialogsUsers {
let telegramUser = TelegramUser(user: user)
peers.append(telegramUser)
if let presence = TelegramUserPresence(apiUser: user) {
peerPresences[telegramUser.id] = presence
}
}
let locallyRemovedFromRemoteItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(localItemIdsWithoutSecretChats))
let remotelyRemovedItemIds = Set(initialRemoteItemIdsWithoutSecretChats).subtracting(Set(remoteItemIds))

View File

@ -1,413 +0,0 @@
import Foundation
public protocol Identifiable {
associatedtype T: Hashable
var stableId: T { get }
}
public func mergeListsStable<T>(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)]) where T: Comparable, T: Identifiable {
var removeIndices: [Int] = []
var insertItems: [(Int, T, Int?)] = []
var currentList = leftList
var i = 0
var j = 0
while true {
let left: T? = i < currentList.count ? currentList[i] : nil
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left == right {
i += 1
j += 1
} else if left < right {
removeIndices.append(i)
i += 1
} else {
j += 1
}
} else if let _ = left {
removeIndices.append(i)
i += 1
} else if let _ = right {
j += 1
} else {
break
}
}
for index in removeIndices.reversed() {
currentList.remove(at: index)
}
var previousIndices: [T.T: Int] = [:]
i = 0
for left in leftList {
previousIndices[left.stableId] = i
i += 1
}
i = 0
j = 0
while true {
let left: T? = i < currentList.count ? currentList[i] : nil
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left == right {
i += 1
j += 1
} else if left > right {
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
i += 1
j += 1
} else {
i += 1
}
} else if let _ = left {
i += 1
} else if let right = right {
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
i += 1
j += 1
} else {
break
}
}
assert(currentList == rightList, "currentList == rightList")
return (removeIndices, insertItems)
}
public func mergeListsStableWithUpdates<T>(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)], [(Int, T, Int)]) where T: Comparable, T: Identifiable {
var removeIndices: [Int] = []
var insertItems: [(Int, T, Int?)] = []
var updatedIndices: [(Int, T, Int)] = []
#if (arch(i386) || arch(x86_64)) && os(iOS)
var existingStableIds: [T.T: T] = [:]
for item in leftList {
if let _ = existingStableIds[item.stableId] {
assertionFailure()
} else {
existingStableIds[item.stableId] = item
}
}
existingStableIds.removeAll()
for item in rightList {
if let other = existingStableIds[item.stableId] {
print("\(other) has the same stableId as \(item): \(item.stableId)")
assertionFailure()
} else {
existingStableIds[item.stableId] = item
}
}
#endif
var currentList = leftList
var i = 0
var previousIndices: [T.T: Int] = [:]
for left in leftList {
previousIndices[left.stableId] = i
i += 1
}
i = 0
var j = 0
while true {
let left: T? = i < currentList.count ? currentList[i] : nil
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left.stableId == right.stableId && left != right {
updatedIndices.append((i, right, previousIndices[left.stableId]!))
i += 1
j += 1
} else {
if left == right {
i += 1
j += 1
} else if left < right {
removeIndices.append(i)
i += 1
} else if !(left > right) {
removeIndices.append(i)
i += 1
} else {
j += 1
}
}
} else if let _ = left {
removeIndices.append(i)
i += 1
} else if let _ = right {
j += 1
} else {
break
}
}
//print("remove:\n\(removeIndices)")
for index in removeIndices.reversed() {
currentList.remove(at: index)
for i in 0 ..< updatedIndices.count {
if updatedIndices[i].0 >= index {
updatedIndices[i].0 -= 1
}
}
}
/*print("\n current after removes:\n")
m = 0
for right in currentList {
print("\(m): \(right.stableId)")
m += 1
}
print("update:\n\(updatedIndices.map({ "\($0.0), \($0.1.stableId) (was \($0.2)))" }))")*/
i = 0
j = 0
var k = 0
while true {
let left: T?
//print("i=\(i), j=\(j), k=\(k)")
if k < updatedIndices.count && updatedIndices[k].0 < i {
//print("updated[k=\(k)]=\(updatedIndices[k].0)<i=\(i), k++")
k += 1
}
if k < updatedIndices.count {
if updatedIndices[k].0 == i {
left = updatedIndices[k].1
//print("override left = \(updatedIndices[k].1.stableId)")
} else {
left = i < currentList.count ? currentList[i] : nil
}
} else {
left = i < currentList.count ? currentList[i] : nil
}
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left == right {
//print("\(left.stableId)==\(right.stableId)")
//print("i++, j++")
i += 1
j += 1
} else if left > right {
//print("\(left.stableId)>\(right.stableId)")
//print("insert \(right.stableId) at \(i)")
//print("i++, j++")
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
if k < updatedIndices.count {
for l in k ..< updatedIndices.count {
updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2)
}
}
i += 1
j += 1
} else {
//print("\(left.stableId)<\(right.stableId)")
//print("i++")
i += 1
}
} else if let _ = left {
//print("\(left!.stableId)>nil")
//print("i++")
i += 1
} else if let right = right {
//print("nil<\(right.stableId)")
//print("insert \(right.stableId) at \(i)")
//print("i++")
//print("j++")
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
if k < updatedIndices.count {
for l in k ..< updatedIndices.count {
updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2)
}
}
i += 1
j += 1
} else {
break
}
}
for (index, item, _) in updatedIndices {
currentList[index] = item
}
assert(currentList == rightList, "currentList == rightList")
return (removeIndices, insertItems, updatedIndices)
}
public func mergeListsStableWithUpdatesReversed<T>(leftList: [T], rightList: [T]) -> ([Int], [(Int, T, Int?)], [(Int, T, Int)]) where T: Comparable, T: Identifiable {
var removeIndices: [Int] = []
var insertItems: [(Int, T, Int?)] = []
var updatedIndices: [(Int, T, Int)] = []
#if (arch(i386) || arch(x86_64)) && os(iOS)
var existingStableIds: [T.T: T] = [:]
for item in leftList {
if let _ = existingStableIds[item.stableId] {
assertionFailure()
} else {
existingStableIds[item.stableId] = item
}
}
existingStableIds.removeAll()
for item in rightList {
if let other = existingStableIds[item.stableId] {
print("\(other) has the same stableId as \(item): \(item.stableId)")
assertionFailure()
} else {
existingStableIds[item.stableId] = item
}
}
#endif
var currentList = leftList
var i = 0
var previousIndices: [T.T: Int] = [:]
for left in leftList {
previousIndices[left.stableId] = i
i += 1
}
i = 0
var j = 0
while true {
let left: T? = i < currentList.count ? currentList[i] : nil
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left.stableId == right.stableId && left != right {
updatedIndices.append((i, right, previousIndices[left.stableId]!))
i += 1
j += 1
} else {
if left == right {
i += 1
j += 1
} else if left > right {
removeIndices.append(i)
i += 1
} else if !(left < right) {
removeIndices.append(i)
i += 1
} else {
j += 1
}
}
} else if let _ = left {
removeIndices.append(i)
i += 1
} else if let _ = right {
j += 1
} else {
break
}
}
//print("remove:\n\(removeIndices)")
for index in removeIndices.reversed() {
currentList.remove(at: index)
for i in 0 ..< updatedIndices.count {
if updatedIndices[i].0 >= index {
updatedIndices[i].0 -= 1
}
}
}
i = 0
j = 0
var k = 0
while true {
let left: T?
if k < updatedIndices.count && updatedIndices[k].0 < i {
k += 1
}
if k < updatedIndices.count {
if updatedIndices[k].0 == i {
left = updatedIndices[k].1
} else {
left = i < currentList.count ? currentList[i] : nil
}
} else {
left = i < currentList.count ? currentList[i] : nil
}
let right: T? = j < rightList.count ? rightList[j] : nil
if let left = left, let right = right {
if left == right {
i += 1
j += 1
} else if left < right {
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
if k < updatedIndices.count {
for l in k ..< updatedIndices.count {
updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2)
}
}
i += 1
j += 1
} else {
i += 1
}
} else if let _ = left {
i += 1
} else if let right = right {
let previousIndex = previousIndices[right.stableId]
insertItems.append((i, right, previousIndex))
currentList.insert(right, at: i)
if k < updatedIndices.count {
for l in k ..< updatedIndices.count {
updatedIndices[l] = (updatedIndices[l].0 + 1, updatedIndices[l].1, updatedIndices[l].2)
}
}
i += 1
j += 1
} else {
break
}
}
for (index, item, _) in updatedIndices {
currentList[index] = item
}
assert(currentList == rightList, "currentList == rightList")
return (removeIndices, insertItems, updatedIndices)
}

View File

@ -122,7 +122,10 @@ public func outgoingMessageWithChatContextResult(_ results: ChatContextResultCol
attributes.append(InlineBotMessageAttribute(peerId: results.botId))
switch result.message {
case let .auto(caption, replyMarkup):
case let .auto(caption, entities, replyMarkup):
if let entities = entities {
attributes.append(entities)
}
if let replyMarkup = replyMarkup {
attributes.append(replyMarkup)
}

View File

@ -551,8 +551,20 @@ public final class PendingMessageManager {
}
if let uniqueId = uniqueId {
switch content {
case let .media(inputMedia):
singleMedias.append(.inputSingleMedia(media: inputMedia, randomId: uniqueId))
case let .media(inputMedia, text):
var messageEntities: [Api.MessageEntity]?
for attribute in message.attributes {
if let attribute = attribute as? TextEntitiesMessageAttribute {
messageEntities = apiTextAttributeEntities(attribute, associatedPeers: message.peers)
}
}
var singleFlags: Int32 = 0
if let _ = messageEntities {
singleFlags |= 1 << 0
}
singleMedias.append(.inputSingleMedia(media: inputMedia, flags: singleFlags, randomId: uniqueId, message: text, entities: messageEntities))
default:
return .complete()
}
@ -726,8 +738,8 @@ public final class PendingMessageManager {
|> mapError { _ -> NoError in
return NoError()
}
case let .media(inputMedia):
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil), tag: dependencyTag)
case let .media(inputMedia, text):
sendMessageRequest = network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities), tag: dependencyTag)
|> mapError { _ -> NoError in
return NoError()
}

View File

@ -11,7 +11,7 @@ import TelegramCorePrivateModule
enum PendingMessageUploadedContent {
case text(String)
case media(Api.InputMedia)
case media(Api.InputMedia, String)
case forward(ForwardSourceInfoAttribute)
case chatContextResult(OutgoingChatContextResultMessageAttribute)
case secretMedia(Api.InputEncryptedFile, Int32, SecretFileEncryptionKey)
@ -64,19 +64,19 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
} else if let media = media.first {
if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) {
if let reference = image.reference, case let .cloud(id, accessHash) = reference {
return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: nil)))
return .ready(.media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash), ttlSeconds: nil), text))
} else {
return .upload(uploadedMediaImageContent(network: network, postbox: postbox, peerId: peerId, image: image, text: text, autoremoveAttribute: autoremoveAttribute))
}
} else if let file = media as? TelegramMediaFile {
if let resource = file.resource as? CloudDocumentMediaResource {
return .ready(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text)))
return .ready(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text))
} else {
return .upload(uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, peerId: peerId, messageId: messageId, text: text, attributes: attributes, file: file))
}
} else if let contact = media as? TelegramMediaContact {
let input = Api.InputMedia.inputMediaContact(phoneNumber: contact.phoneNumber, firstName: contact.firstName, lastName: contact.lastName)
return .ready(.media(input))
return .ready(.media(input, text))
} else if let map = media as? TelegramMediaMap {
let input: Api.InputMedia
if let liveBroadcastingTimeout = map.liveBroadcastingTimeout {
@ -86,7 +86,7 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods
} else {
input = .inputMediaGeoPoint(geoPoint: Api.InputGeoPoint.inputGeoPoint(lat: map.latitude, long: map.longitude))
}
return .ready(.media(input))
return .ready(.media(input, text))
} else {
return .ready(.text(text))
}
@ -194,7 +194,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
flags |= 1 << 1
ttlSeconds = autoremoveAttribute.timeout
}
return .single(.progress(1.0)) |> then(.single(.content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: ttlSeconds)))))
return .single(.progress(1.0)) |> then(.single(.content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text))))
}
case let .localReference(key):
referenceKey = key
@ -221,14 +221,14 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let inputPeer = inputPeer {
if autoremoveAttribute != nil {
return .single(.content(.media(.inputMediaUploadedPhoto(flags: flags, file: file, caption: text, stickers: nil, ttlSeconds: ttlSeconds))))
return .single(.content(.media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds), text)))
}
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, caption: text, stickers: nil, ttlSeconds: ttlSeconds)))
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds)))
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
switch result {
case let .messageMediaPhoto(_, photo, _, _):
case let .messageMediaPhoto(_, photo, _):
if let photo = photo, let mediaImage = telegramMediaImageFromApiPhoto(photo), let reference = mediaImage.reference, case let .cloud(id, accessHash) = reference {
var flags: Int32 = 0
var ttlSeconds: Int32?
@ -236,7 +236,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, peerI
flags |= 1 << 1
ttlSeconds = autoremoveAttribute.timeout
}
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), caption: text, ttlSeconds: ttlSeconds))), media: mediaImage)
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaPhoto(flags: flags, id: .inputPhoto(id: id, accessHash: accessHash), ttlSeconds: ttlSeconds), text)), media: mediaImage)
}
default:
break
@ -359,7 +359,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
switch result {
case let .media(media):
if let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
return .single(.progress(1.0)) |> then(.single(.content(.media(Api.InputMedia.inputMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text)))))
return .single(.progress(1.0)) |> then(.single(.content(.media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text))))
}
case let .localReference(key):
referenceKey = key
@ -460,7 +460,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
}
if ttlSeconds != nil {
return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: ttlSeconds))))
return .single(.content(.media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds), text)))
}
return postbox.modify { modifier -> Api.InputPeer? in
@ -469,13 +469,13 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let inputPeer = inputPeer {
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), caption: text, stickers: nil, ttlSeconds: ttlSeconds)))
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnail, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: ttlSeconds)))
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
switch result {
case let .messageMediaDocument(_, document, _, _):
case let .messageMediaDocument(_, document, _):
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document), let resource = mediaFile.resource as? CloudDocumentMediaResource {
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaDocument(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash), caption: text))), media: mediaFile)
return maybeCacheUploadedResource(postbox: postbox, key: referenceKey, result: .content(.media(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash), ttlSeconds: nil), text)), media: mediaFile)
}
default:
break

View File

@ -64,7 +64,7 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra
}
}
} else if let peer = peer, let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0)) |> map {Optional($0)}
return account.network.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: .inputMessagesFilterChatPhotos, minDate: 0, maxDate: 0, offsetId: 0, addOffset: 0, limit: 1000, maxId: 0, minId: 0, hash: 0)) |> map {Optional($0)}
|> mapError {_ in}
|> `catch` {
return Signal<Api.messages.Messages?, Void>.single(nil)
@ -75,18 +75,22 @@ public func requestPeerPhotos(account:Account, peerId:PeerId) -> Signal<[Telegra
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
messages = []
chats = []
users = []
}
return account.postbox.modify { modifier -> [Message] in

View File

@ -79,7 +79,7 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
flags |= (1 << 0)
}
}
return account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: 0, addOffset: 0, limit: 100, maxId: Int32.max - 1, minId: 0))
return account.network.request(Api.functions.messages.search(flags: flags, peer: inputPeer, q: query, fromId: fromInputUser, filter: filter, minDate: 0, maxDate: Int32.max - 1, offsetId: 0, addOffset: 0, limit: 100, maxId: Int32.max - 1, minId: 0, hash: 0))
|> map {Optional($0)}
|> `catch` { _ -> Signal<Api.messages.Messages?, MTRpcError> in
return .single(nil)
@ -117,6 +117,10 @@ public func searchMessages(account: Account, location: SearchMessagesLocation, q
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
messages = []
chats = []
users = []
}
return account.postbox.modify { modifier -> [Message] in
@ -183,18 +187,22 @@ public func downloadMessage(account: Account, messageId: MessageId) -> Signal<Me
let chats: [Api.Chat]
let users: [Api.User]
switch result {
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .channelMessages(_, _, _, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let .messages(apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case let.messagesSlice(_, apiMessages, apiChats, apiUsers):
messages = apiMessages
chats = apiChats
users = apiUsers
case .messagesNotModified:
messages = []
chats = []
users = []
}
let postboxSignal = account.postbox.modify { modifier -> Message? in
@ -239,7 +247,7 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
if peerId.namespace == Namespaces.Peer.SecretChat {
return .single(modifier.findClosestMessageIdByTimestamp(peerId: peerId, timestamp: timestamp))
} else if let peer = modifier.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0))
return account.network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: 0, offsetDate: timestamp, addOffset: -1, limit: 1, maxId: 0, minId: 0, hash: 0))
|> map { result -> MessageId? in
let messages: [Api.Message]
switch result {
@ -249,6 +257,8 @@ public func searchMessageIdByTimestamp(account: Account, peerId: PeerId, timesta
messages = apiMessages
case let.messagesSlice(_, apiMessages, _, _):
messages = apiMessages
case .messagesNotModified:
messages = []
}
for message in messages {
if let message = StoreMessage(apiMessage: message), case let .Id(id) = message.id {

View File

@ -20,7 +20,7 @@ public class BoxedMessage: NSObject {
public class Serialization: NSObject, MTSerialization {
public func currentLayer() -> UInt {
return 75
return 76
}
public func parseMessage(_ data: Data!) -> Any! {

View File

@ -64,6 +64,10 @@ private func fetchMessage(modifier: Modifier, account: Account, messageId: Messa
apiMessages = messages
apiChats = chats
apiUsers = users
case .messagesNotModified:
apiMessages = []
apiChats = []
apiUsers = []
}
var peers: [PeerId: Peer] = [:]

View File

@ -16,7 +16,7 @@ public enum StandaloneMedia {
private enum StandaloneMessageContent {
case text(String)
case media(Api.InputMedia)
case media(Api.InputMedia, String)
}
private enum StandaloneSendMessageEvent {
@ -33,25 +33,25 @@ public func standaloneSendMessage(account: Account, peerId: PeerId, text: String
if let media = media {
switch media {
case let .image(data):
content = uploadedImage(account: account, text: text, data: data)
content = uploadedImage(account: account, data: data)
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|> map { next -> StandaloneSendMessageEvent in
switch next {
case let .progress(progress):
return .progress(progress)
case let .result(media):
return .result(.media(media))
return .result(.media(media, text))
}
}
case let .file(data, mimeType, attributes):
content = uploadedFile(account: account, text: text, data: data, mimeType: mimeType, attributes: attributes)
content = uploadedFile(account: account, data: data, mimeType: mimeType, attributes: attributes)
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|> map { next -> StandaloneSendMessageEvent in
switch next {
case let .progress(progress):
return .progress(progress)
case let .result(media):
return .result(.media(media))
return .result(.media(media, text))
}
}
}
@ -116,8 +116,8 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M
|> mapError { _ -> NoError in
return NoError()
}
case let .media(inputMedia):
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, randomId: uniqueId, replyMarkup: nil))
case let .media(inputMedia, text):
sendMessageRequest = account.network.request(Api.functions.messages.sendMedia(flags: flags, peer: inputPeer, replyToMsgId: replyMessageId, media: inputMedia, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities))
|> mapError { _ -> NoError in
return NoError()
}
@ -141,13 +141,13 @@ private enum UploadMediaEvent {
case result(Api.InputMedia)
}
private func uploadedImage(account: Account, text: String, data: Data) -> Signal<UploadMediaEvent, StandaloneSendMessageError> {
private func uploadedImage(account: Account, data: Data) -> Signal<UploadMediaEvent, StandaloneSendMessageError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image))
|> mapError { _ -> StandaloneSendMessageError in return .generic }
|> map { next -> UploadMediaEvent in
switch next {
case let .inputFile(inputFile):
return .result(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, caption: text, stickers: nil, ttlSeconds: nil))
return .result(Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil))
case .inputSecretFile:
preconditionFailure()
case let .progress(progress):
@ -156,13 +156,13 @@ private func uploadedImage(account: Account, text: String, data: Data) -> Signal
}
}
private func uploadedFile(account: Account, text: String, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal<UploadMediaEvent, PendingMessageUploadError> {
private func uploadedFile(account: Account, data: Data, mimeType: String, attributes: [TelegramMediaFileAttribute]) -> Signal<UploadMediaEvent, PendingMessageUploadError> {
return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: statsCategoryForFileWithAttributes(attributes)))
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> map { next -> UploadMediaEvent in
switch next {
case let .inputFile(inputFile):
return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), caption: text, stickers: nil, ttlSeconds: nil))
return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil))
case .inputSecretFile:
preconditionFailure()
case let .progress(progress):

View File

@ -28,11 +28,11 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
if let inputPeer = inputPeer {
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, caption: "", stickers: nil, ttlSeconds: nil)))
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: 0, file: inputFile, stickers: nil, ttlSeconds: nil)))
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch media {
case let .messageMediaPhoto(_, photo, _, _):
case let .messageMediaPhoto(_, photo, _):
if let photo = photo {
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
return .single(.result(mediaImage))
@ -67,11 +67,11 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { inputPeer -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
if let inputPeer = inputPeer {
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), caption: text, stickers: nil, ttlSeconds: nil)))
return account.network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch media {
case let .messageMediaDocument(_, document, _, _):
case let .messageMediaDocument(_, document, _):
if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document) {
return .single(.result(mediaFile))

View File

@ -266,48 +266,48 @@ extension Api.Message {
}
}
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (String?, Media?, Int32?) {
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?) {
if let media = media {
switch media {
case let .messageMediaPhoto(_, photo, caption, ttlSeconds):
case let .messageMediaPhoto(_, photo, ttlSeconds):
if let photo = photo {
if let mediaImage = telegramMediaImageFromApiPhoto(photo) {
return (caption, mediaImage, ttlSeconds)
return (mediaImage, ttlSeconds)
}
} else {
return (nil, TelegramMediaExpiredContent(data: .image), nil)
return (TelegramMediaExpiredContent(data: .image), nil)
}
case let .messageMediaContact(phoneNumber, firstName, lastName, userId):
let contactPeerId: PeerId? = userId == 0 ? nil : PeerId(namespace: Namespaces.Peer.CloudUser, id: userId)
let mediaContact = TelegramMediaContact(firstName: firstName, lastName: lastName, phoneNumber: phoneNumber, peerId: contactPeerId)
return (nil, mediaContact, nil)
return (mediaContact, nil)
case let .messageMediaGeo(geo):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: nil)
return (nil, mediaMap, nil)
return (mediaMap, nil)
case let .messageMediaVenue(geo, title, address, provider, venueId, venueType):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: title, address: address, provider: provider, venueId: venueId, venueType: venueType, liveBroadcastingTimeout: nil)
return (nil, mediaMap, nil)
return (mediaMap, nil)
case let .messageMediaGeoLive(geo, period):
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period)
return (nil, mediaMap, nil)
case let .messageMediaDocument(_, document, caption, ttlSeconds):
return (mediaMap, nil)
case let .messageMediaDocument(_, document, ttlSeconds):
if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document) {
return (caption, mediaFile, ttlSeconds)
return (mediaFile, ttlSeconds)
}
} else {
return (nil, TelegramMediaExpiredContent(data: .file), nil)
return (TelegramMediaExpiredContent(data: .file), nil)
}
case let .messageMediaWebPage(webpage):
if let mediaWebpage = telegramMediaWebpageFromApiWebpage(webpage) {
return (nil, mediaWebpage, nil)
return (mediaWebpage, nil)
}
case .messageMediaUnsupported:
return (nil, TelegramMediaUnsupported(), nil)
return (TelegramMediaUnsupported(), nil)
case .messageMediaEmpty:
break
case let .messageMediaGame(game):
return (nil, TelegramMediaGame(apiGame: game), nil)
return (TelegramMediaGame(apiGame: game), nil)
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
var parsedFlags = TelegramMediaInvoiceFlags()
if (flags & (1 << 3)) != 0 {
@ -316,11 +316,11 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
if (flags & (1 << 1)) != 0 {
parsedFlags.insert(.shippingAddressRequested)
}
return (nil, TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil)
return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil)
}
}
return (nil, nil, nil)
return (nil, nil)
}
func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [MessageTextEntity] {
@ -432,16 +432,13 @@ extension StoreMessage {
}
}
var messageText = message
let messageText = message
var medias: [Media] = []
var consumableContent: (Bool, Bool)? = nil
if let media = media {
let (mediaText, mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let mediaText = mediaText {
messageText = mediaText
}
let (mediaValue, expirationTimer) = textMediaAndExpirationTimerFromApiMedia(media, peerId)
if let mediaValue = mediaValue {
medias.append(mediaValue)

View File

@ -42,7 +42,7 @@ private func inputSecretChat(postbox: Postbox, peerId: PeerId) -> Signal<Api.Inp
private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId) -> Signal<(Int32, Int32), VerifyReadStateError> {
return inputPeer(postbox: postbox, peerId: peerId)
|> mapToSignal { inputPeer -> Signal<(Int32, Int32), VerifyReadStateError> in
return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: Int32.max, offsetDate: Int32.max, addOffset: 0, limit: 1, maxId: Int32.max, minId: 1))
return network.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: Int32.max, offsetDate: Int32.max, addOffset: 0, limit: 1, maxId: Int32.max, minId: 1, hash: 0))
|> retryRequest
|> mapToSignalPromotingError { result -> Signal<(Int32, Int32), VerifyReadStateError> in
let apiMessages: [Api.Message]
@ -53,6 +53,8 @@ private func dialogTopMessage(network: Network, postbox: Postbox, peerId: PeerId
apiMessages = messages
case let .messagesSlice(_, messages, _, _):
apiMessages = messages
case .messagesNotModified:
apiMessages = []
}
if let message = apiMessages.first, let timestamp = message.timestamp {
return .single((message.rawId, timestamp))

View File

@ -8,7 +8,7 @@ import Foundation
extension Api.MessageMedia {
var preCachedResources: [(MediaResource, Data)]? {
switch self {
case let .messageMediaPhoto(_, photo, _, _):
case let .messageMediaPhoto(_, photo, _):
if let photo = photo {
switch photo {
case let .photo(_, _, _, _, sizes):
@ -34,7 +34,7 @@ extension Api.MessageMedia {
} else {
return nil
}
case let .messageMediaDocument(_, document, _, _):
case let .messageMediaDocument(_, document, _):
if let document = document {
switch document {
case .document:
@ -283,6 +283,17 @@ extension Api.Update {
}
return nil
}
var channelPts: Int32? {
switch self {
case let .updateNewChannelMessage(_, pts, _):
return pts
case let .updateEditChannelMessage(_, pts, _):
return pts
default:
return nil
}
}
}
extension Api.Updates {
@ -421,6 +432,39 @@ extension Api.Updates {
return []
}
}
var channelPts: Int32? {
switch self {
case let .updates(updates, _, _, _, _):
var result: Int32?
for update in updates {
if let channelPts = update.channelPts {
if result == nil || channelPts > result! {
result = channelPts
}
}
}
return result
case let .updatesCombined(updates, _, _, _, _, _):
var result: Int32?
for update in updates {
if let channelPts = update.channelPts {
if result == nil || channelPts > result! {
result = channelPts
}
}
}
return result
case let .updateShort(update, _):
if let message = update.channelPts {
return channelPts
} else {
return nil
}
default:
return nil
}
}
}
extension Api.Updates {

View File

@ -14,7 +14,7 @@ public func webpagePreview(account: Account, url: String, webpageId: MediaId? =
if let webpageId = webpageId, let webpage = modifier.getMedia(webpageId) as? TelegramMediaWebpage {
return .single(webpage)
} else {
return account.network.request(Api.functions.messages.getWebPagePreview(message: url))
return account.network.request(Api.functions.messages.getWebPagePreview(flags: 0, message: url, entities: nil))
|> `catch` { _ -> Signal<Api.MessageMedia, NoError> in
return .single(.messageMediaEmpty)
}