mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-07-04 18:41:00 +00:00
Initial implementation for reactions API
This commit is contained in:
parent
0f72e95e24
commit
8f463038e4
@ -1242,6 +1242,7 @@ public class Account {
|
|||||||
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
self.managedOperationsDisposable.add(managedSynchronizeConsumeMessageContentOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||||
self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
self.managedOperationsDisposable.add(managedConsumePersonalMessagesActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||||
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
self.managedOperationsDisposable.add(managedSynchronizeMarkAllUnseenPersonalMessagesOperations(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||||
|
self.managedOperationsDisposable.add(managedApplyPendingMessageReactionsActions(postbox: self.postbox, network: self.network, stateManager: self.stateManager).start())
|
||||||
self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start())
|
self.managedOperationsDisposable.add(managedSynchronizeEmojiKeywordsOperations(postbox: self.postbox, network: self.network).start())
|
||||||
|
|
||||||
let importantBackgroundOperations: [Signal<AccountRunningImportantTasks, NoError>] = [
|
let importantBackgroundOperations: [Signal<AccountRunningImportantTasks, NoError>] = [
|
||||||
|
@ -40,6 +40,7 @@ private var declaredEncodables: Void = {
|
|||||||
declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) })
|
declareEncodable(TextEntitiesMessageAttribute.self, f: { TextEntitiesMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) })
|
declareEncodable(ReplyMessageAttribute.self, f: { ReplyMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(ReactionsMessageAttribute.self, f: { ReactionsMessageAttribute(decoder: $0) })
|
declareEncodable(ReactionsMessageAttribute.self, f: { ReactionsMessageAttribute(decoder: $0) })
|
||||||
|
declareEncodable(PendingReactionsMessageAttribute.self, f: { PendingReactionsMessageAttribute(decoder: $0) })
|
||||||
declareEncodable(CloudDocumentMediaResource.self, f: { CloudDocumentMediaResource(decoder: $0) })
|
declareEncodable(CloudDocumentMediaResource.self, f: { CloudDocumentMediaResource(decoder: $0) })
|
||||||
declareEncodable(TelegramMediaWebpage.self, f: { TelegramMediaWebpage(decoder: $0) })
|
declareEncodable(TelegramMediaWebpage.self, f: { TelegramMediaWebpage(decoder: $0) })
|
||||||
declareEncodable(ViewCountMessageAttribute.self, f: { ViewCountMessageAttribute(decoder: $0) })
|
declareEncodable(ViewCountMessageAttribute.self, f: { ViewCountMessageAttribute(decoder: $0) })
|
||||||
@ -149,6 +150,7 @@ private var declaredEncodables: Void = {
|
|||||||
declareEncodable(CloudStickerPackThumbnailMediaResource.self, f: { CloudStickerPackThumbnailMediaResource(decoder: $0) })
|
declareEncodable(CloudStickerPackThumbnailMediaResource.self, f: { CloudStickerPackThumbnailMediaResource(decoder: $0) })
|
||||||
declareEncodable(AccountBackupDataAttribute.self, f: { AccountBackupDataAttribute(decoder: $0) })
|
declareEncodable(AccountBackupDataAttribute.self, f: { AccountBackupDataAttribute(decoder: $0) })
|
||||||
declareEncodable(ContentRequiresValidationMessageAttribute.self, f: { ContentRequiresValidationMessageAttribute(decoder: $0) })
|
declareEncodable(ContentRequiresValidationMessageAttribute.self, f: { ContentRequiresValidationMessageAttribute(decoder: $0) })
|
||||||
|
declareEncodable(UpdateMessageReactionsAction.self, f: { UpdateMessageReactionsAction(decoder: $0) })
|
||||||
|
|
||||||
return
|
return
|
||||||
}()
|
}()
|
||||||
|
@ -647,8 +647,7 @@ func finalStateWithDifference(postbox: Postbox, network: Network, state: Account
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
||||||
var result: [Api.Update] = []
|
var otherUpdates: [Api.Update] = []
|
||||||
|
|
||||||
var updatesByChannel: [PeerId: [Api.Update]] = [:]
|
var updatesByChannel: [PeerId: [Api.Update]] = [:]
|
||||||
|
|
||||||
for update in updates {
|
for update in updates {
|
||||||
@ -675,7 +674,7 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
|||||||
updatesByChannel[peerId]!.append(update)
|
updatesByChannel[peerId]!.append(update)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.append(update)
|
otherUpdates.append(update)
|
||||||
}
|
}
|
||||||
case let .updateEditChannelMessage(message, _, _):
|
case let .updateEditChannelMessage(message, _, _):
|
||||||
if let peerId = apiMessagePeerId(message) {
|
if let peerId = apiMessagePeerId(message) {
|
||||||
@ -685,7 +684,7 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
|||||||
updatesByChannel[peerId]!.append(update)
|
updatesByChannel[peerId]!.append(update)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.append(update)
|
otherUpdates.append(update)
|
||||||
}
|
}
|
||||||
case let .updateChannelWebPage(channelId, _, _, _):
|
case let .updateChannelWebPage(channelId, _, _, _):
|
||||||
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
let peerId = PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId)
|
||||||
@ -702,10 +701,12 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
|||||||
updatesByChannel[peerId]!.append(update)
|
updatesByChannel[peerId]!.append(update)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
result.append(update)
|
otherUpdates.append(update)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var result: [Api.Update] = []
|
||||||
|
|
||||||
for (_, updates) in updatesByChannel {
|
for (_, updates) in updatesByChannel {
|
||||||
let sortedUpdates = updates.sorted(by: { lhs, rhs in
|
let sortedUpdates = updates.sorted(by: { lhs, rhs in
|
||||||
var lhsPts: Int32?
|
var lhsPts: Int32?
|
||||||
@ -747,6 +748,7 @@ private func sortedUpdates(_ updates: [Api.Update]) -> [Api.Update] {
|
|||||||
})
|
})
|
||||||
result.append(contentsOf: sortedUpdates)
|
result.append(contentsOf: sortedUpdates)
|
||||||
}
|
}
|
||||||
|
result.append(contentsOf: otherUpdates)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -7,16 +7,20 @@ import Foundation
|
|||||||
|
|
||||||
public class EditedMessageAttribute: MessageAttribute {
|
public class EditedMessageAttribute: MessageAttribute {
|
||||||
public let date: Int32
|
public let date: Int32
|
||||||
|
public let isHidden: Bool
|
||||||
|
|
||||||
init(date: Int32) {
|
init(date: Int32, isHidden: Bool) {
|
||||||
self.date = date
|
self.date = date
|
||||||
|
self.isHidden = isHidden
|
||||||
}
|
}
|
||||||
|
|
||||||
required public init(decoder: PostboxDecoder) {
|
required public init(decoder: PostboxDecoder) {
|
||||||
self.date = decoder.decodeInt32ForKey("d", orElse: 0)
|
self.date = decoder.decodeInt32ForKey("d", orElse: 0)
|
||||||
|
self.isHidden = decoder.decodeInt32ForKey("h", orElse: 0) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
encoder.encodeInt32(self.date, forKey: "d")
|
encoder.encodeInt32(self.date, forKey: "d")
|
||||||
|
encoder.encodeInt32(self.isHidden ? 1 : 0, forKey: "h")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,29 +15,241 @@ import MtProtoKitDynamic
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
final class UpdateMessageReactionsAction: PendingMessageActionData {
|
||||||
|
init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
init(decoder: PostboxDecoder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(_ encoder: PostboxEncoder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEqual(to: PendingMessageActionData) -> Bool {
|
||||||
|
if let _ = to as? UpdateMessageReactionsAction {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum RequestUpdateMessageReactionError {
|
public func updateMessageReactionsInteractively(postbox: Postbox, messageId: MessageId, reactions: [String]) -> Signal<Never, NoError> {
|
||||||
|
return postbox.transaction { transaction -> Void in
|
||||||
|
transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: UpdateMessageReactionsAction())
|
||||||
|
transaction.updateMessage(messageId, 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
|
||||||
|
loop: for j in 0 ..< attributes.count {
|
||||||
|
if let _ = attributes[j] as? PendingReactionsMessageAttribute {
|
||||||
|
attributes.remove(at: j)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attributes.append(PendingReactionsMessageAttribute(values: reactions))
|
||||||
|
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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum RequestUpdateMessageReactionError {
|
||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
|
|
||||||
public func requestUpdateMessageReaction(account: Account, messageId: MessageId, reactions: [String]) -> Signal<Never, RequestUpdateMessageReactionError> {
|
private func requestUpdateMessageReaction(postbox: Postbox, network: Network, stateManager: AccountStateManager, messageId: MessageId) -> Signal<Never, RequestUpdateMessageReactionError> {
|
||||||
return account.postbox.loadedPeerWithId(messageId.peerId)
|
return postbox.transaction { transaction -> (Peer, [String])? in
|
||||||
|> take(1)
|
guard let peer = transaction.getPeer(messageId.peerId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let message = transaction.getMessage(messageId) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var values: [String] = []
|
||||||
|
for attribute in message.attributes {
|
||||||
|
if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||||
|
values = attribute.values
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (peer, values)
|
||||||
|
}
|
||||||
|> introduceError(RequestUpdateMessageReactionError.self)
|
|> introduceError(RequestUpdateMessageReactionError.self)
|
||||||
|> mapToSignal { peer in
|
|> mapToSignal { peerAndValues in
|
||||||
|
guard let (peer, values) = peerAndValues else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
guard let inputPeer = apiInputPeer(peer) else {
|
guard let inputPeer = apiInputPeer(peer) else {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
if messageId.namespace != Namespaces.Message.Cloud {
|
if messageId.namespace != Namespaces.Message.Cloud {
|
||||||
return .fail(.generic)
|
return .fail(.generic)
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.sendReaction(peer: inputPeer, msgId: messageId.id, reaction: reactions))
|
return network.request(Api.functions.messages.sendReaction(peer: inputPeer, msgId: messageId.id, reaction: values))
|
||||||
|> mapError { _ -> RequestUpdateMessageReactionError in
|
|> mapError { _ -> RequestUpdateMessageReactionError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<Never, RequestUpdateMessageReactionError> in
|
|> mapToSignal { result -> Signal<Never, RequestUpdateMessageReactionError> in
|
||||||
account.stateManager.addUpdates(result)
|
return postbox.transaction { transaction -> Void in
|
||||||
return .complete()
|
transaction.setPendingMessageAction(type: .updateReaction, id: messageId, action: UpdateMessageReactionsAction())
|
||||||
|
transaction.updateMessage(messageId, 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)
|
||||||
|
}
|
||||||
|
let reactions = mergedMessageReactions(attributes: currentMessage.attributes)
|
||||||
|
var attributes = currentMessage.attributes
|
||||||
|
for j in (0 ..< attributes.count).reversed() {
|
||||||
|
if attributes[j] is PendingReactionsMessageAttribute || attributes[j] is ReactionsMessageAttribute {
|
||||||
|
attributes.remove(at: j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let reactions = reactions {
|
||||||
|
attributes.append(reactions)
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
})
|
||||||
|
stateManager.addUpdates(result)
|
||||||
|
}
|
||||||
|
|> introduceError(RequestUpdateMessageReactionError.self)
|
||||||
|
|> ignoreValues
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class ManagedApplyPendingMessageReactionsActionsHelper {
|
||||||
|
var operationDisposables: [MessageId: Disposable] = [:]
|
||||||
|
|
||||||
|
func update(entries: [PendingMessageActionsEntry]) -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)]) {
|
||||||
|
var disposeOperations: [Disposable] = []
|
||||||
|
var beginOperations: [(PendingMessageActionsEntry, MetaDisposable)] = []
|
||||||
|
|
||||||
|
var hasRunningOperationForPeerId = Set<PeerId>()
|
||||||
|
var validIds = Set<MessageId>()
|
||||||
|
for entry in entries {
|
||||||
|
if !hasRunningOperationForPeerId.contains(entry.id.peerId) {
|
||||||
|
hasRunningOperationForPeerId.insert(entry.id.peerId)
|
||||||
|
validIds.insert(entry.id)
|
||||||
|
|
||||||
|
if self.operationDisposables[entry.id] == nil {
|
||||||
|
let disposable = MetaDisposable()
|
||||||
|
beginOperations.append((entry, disposable))
|
||||||
|
self.operationDisposables[entry.id] = disposable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeMergedIds: [MessageId] = []
|
||||||
|
for (id, disposable) in self.operationDisposables {
|
||||||
|
if !validIds.contains(id) {
|
||||||
|
removeMergedIds.append(id)
|
||||||
|
disposeOperations.append(disposable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for id in removeMergedIds {
|
||||||
|
self.operationDisposables.removeValue(forKey: id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (disposeOperations, beginOperations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() -> [Disposable] {
|
||||||
|
let disposables = Array(self.operationDisposables.values)
|
||||||
|
self.operationDisposables.removeAll()
|
||||||
|
return disposables
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func withTakenAction(postbox: Postbox, type: PendingMessageActionType, id: MessageId, _ f: @escaping (Transaction, PendingMessageActionsEntry?) -> Signal<Never, NoError>) -> Signal<Never, NoError> {
|
||||||
|
return postbox.transaction { transaction -> Signal<Never, NoError> in
|
||||||
|
var result: PendingMessageActionsEntry?
|
||||||
|
|
||||||
|
if let action = transaction.getPendingMessageAction(type: type, id: id) as? UpdateMessageReactionsAction {
|
||||||
|
result = PendingMessageActionsEntry(id: id, action: action)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f(transaction, result)
|
||||||
|
}
|
||||||
|
|> switchToLatest
|
||||||
|
}
|
||||||
|
|
||||||
|
func managedApplyPendingMessageReactionsActions(postbox: Postbox, network: Network, stateManager: AccountStateManager) -> Signal<Void, NoError> {
|
||||||
|
return Signal { _ in
|
||||||
|
let helper = Atomic<ManagedApplyPendingMessageReactionsActionsHelper>(value: ManagedApplyPendingMessageReactionsActionsHelper())
|
||||||
|
|
||||||
|
let actionsKey = PostboxViewKey.pendingMessageActions(type: .updateReaction)
|
||||||
|
let disposable = postbox.combinedView(keys: [actionsKey]).start(next: { view in
|
||||||
|
var entries: [PendingMessageActionsEntry] = []
|
||||||
|
if let v = view.views[actionsKey] as? PendingMessageActionsView {
|
||||||
|
entries = v.entries
|
||||||
|
}
|
||||||
|
|
||||||
|
let (disposeOperations, beginOperations) = helper.with { helper -> (disposeOperations: [Disposable], beginOperations: [(PendingMessageActionsEntry, MetaDisposable)]) in
|
||||||
|
return helper.update(entries: entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
for disposable in disposeOperations {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entry, disposable) in beginOperations {
|
||||||
|
let signal = withTakenAction(postbox: postbox, type: .updateReaction, id: entry.id, { transaction, entry -> Signal<Never, NoError> in
|
||||||
|
if let entry = entry {
|
||||||
|
if let _ = entry.action as? UpdateMessageReactionsAction {
|
||||||
|
return synchronizeMessageReactions(transaction: transaction, postbox: postbox, network: network, stateManager: stateManager, id: entry.id)
|
||||||
|
} else {
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return .complete()
|
||||||
|
})
|
||||||
|
|> then(
|
||||||
|
postbox.transaction { transaction -> Void in
|
||||||
|
transaction.setPendingMessageAction(type: .updateReaction, id: entry.id, action: nil)
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
)
|
||||||
|
|
||||||
|
disposable.set(signal.start())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return ActionDisposable {
|
||||||
|
let disposables = helper.with { helper -> [Disposable] in
|
||||||
|
return helper.reset()
|
||||||
|
}
|
||||||
|
for disposable in disposables {
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
disposable.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func synchronizeMessageReactions(transaction: Transaction, postbox: Postbox, network: Network, stateManager: AccountStateManager, id: MessageId) -> Signal<Never, NoError> {
|
||||||
|
return requestUpdateMessageReaction(postbox: postbox, network: network, stateManager: stateManager, messageId: id)
|
||||||
|
|> `catch` { _ -> Signal<Never, NoError> in
|
||||||
|
return postbox.transaction { transaction -> Void in
|
||||||
|
transaction.setPendingMessageAction(type: .updateReaction, id: id, action: nil)
|
||||||
|
transaction.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
|
||||||
|
loop: for j in 0 ..< attributes.count {
|
||||||
|
if let _ = attributes[j] as? PendingReactionsMessageAttribute {
|
||||||
|
attributes.remove(at: j)
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|> ignoreValues
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -107,6 +107,7 @@ public extension LocalMessageTags {
|
|||||||
|
|
||||||
public extension PendingMessageActionType {
|
public extension PendingMessageActionType {
|
||||||
static let consumeUnseenPersonalMessage = PendingMessageActionType(rawValue: 0)
|
static let consumeUnseenPersonalMessage = PendingMessageActionType(rawValue: 0)
|
||||||
|
static let updateReaction = PendingMessageActionType(rawValue: 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
let peerIdNamespacesWithInitialCloudMessageHoles = [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel]
|
let peerIdNamespacesWithInitialCloudMessageHoles = [Namespaces.Peer.CloudUser, Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel]
|
||||||
@ -135,9 +136,9 @@ public struct OperationLogTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public extension PeerSummaryCounterTags {
|
public extension PeerSummaryCounterTags {
|
||||||
public static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
|
static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0)
|
||||||
public static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
|
static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1)
|
||||||
public static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
|
static let channels = PeerSummaryCounterTags(rawValue: 1 << 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum PreferencesKeyValues: Int32 {
|
private enum PreferencesKeyValues: Int32 {
|
||||||
|
@ -31,7 +31,7 @@ public struct MessageReaction: Equatable, PostboxCoding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ReactionsMessageAttribute: MessageAttribute {
|
public final class ReactionsMessageAttribute: MessageAttribute {
|
||||||
public let reactions: [MessageReaction]
|
public let reactions: [MessageReaction]
|
||||||
|
|
||||||
init(reactions: [MessageReaction]) {
|
init(reactions: [MessageReaction]) {
|
||||||
@ -77,6 +77,72 @@ public class ReactionsMessageAttribute: MessageAttribute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsMessageAttribute? {
|
||||||
|
var current: ReactionsMessageAttribute?
|
||||||
|
var pending: PendingReactionsMessageAttribute?
|
||||||
|
for attribute in attributes {
|
||||||
|
if let attribute = attribute as? ReactionsMessageAttribute {
|
||||||
|
current = attribute
|
||||||
|
} else if let attribute = attribute as? PendingReactionsMessageAttribute {
|
||||||
|
pending = attribute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let pending = pending {
|
||||||
|
var reactions = current?.reactions ?? []
|
||||||
|
for value in pending.values {
|
||||||
|
var found = false
|
||||||
|
for i in 0 ..< reactions.count {
|
||||||
|
if reactions[i].value == value {
|
||||||
|
found = true
|
||||||
|
if !reactions[i].isSelected {
|
||||||
|
reactions[i].isSelected = true
|
||||||
|
reactions[i].count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
reactions.append(MessageReaction(value: value, count: 1, isSelected: true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i in (0 ..< reactions.count).reversed() {
|
||||||
|
if reactions[i].isSelected, !pending.values.contains(reactions[i].value) {
|
||||||
|
if reactions[i].count == 1 {
|
||||||
|
reactions.remove(at: i)
|
||||||
|
} else {
|
||||||
|
reactions[i].isSelected = false
|
||||||
|
reactions[i].count -= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !reactions.isEmpty {
|
||||||
|
return ReactionsMessageAttribute(reactions: reactions)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else if let current = current {
|
||||||
|
return current
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class PendingReactionsMessageAttribute: MessageAttribute {
|
||||||
|
public let values: [String]
|
||||||
|
|
||||||
|
init(values: [String]) {
|
||||||
|
self.values = values
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(decoder: PostboxDecoder) {
|
||||||
|
self.values = decoder.decodeStringArrayForKey("v")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeStringArray(self.values, forKey: "v")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension ReactionsMessageAttribute {
|
extension ReactionsMessageAttribute {
|
||||||
convenience init(apiReactions: Api.MessageReactions) {
|
convenience init(apiReactions: Api.MessageReactions) {
|
||||||
switch apiReactions {
|
switch apiReactions {
|
||||||
|
@ -506,7 +506,7 @@ extension StoreMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let editDate = editDate {
|
if let editDate = editDate {
|
||||||
attributes.append(EditedMessageAttribute(date: editDate))
|
attributes.append(EditedMessageAttribute(date: editDate, isHidden: (flags & (1 << 21)) != 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
var entitiesAttribute: TextEntitiesMessageAttribute?
|
var entitiesAttribute: TextEntitiesMessageAttribute?
|
||||||
|
Loading…
x
Reference in New Issue
Block a user