Merge pull request #676 from RyanRory/emoji-reacts-tweaks

Emoji reacts open group poll & update conflict handling
pull/678/head
RyanZhao 2 years ago committed by GitHub
commit d5a1c310f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -138,6 +138,8 @@
7B7CB192271508AD0079FF93 /* CallRingTonePlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB191271508AD0079FF93 /* CallRingTonePlayer.swift */; };
7B81682328A4C1210069F315 /* UpdateTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682228A4C1210069F315 /* UpdateTypes.swift */; };
7B81682828B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682728B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift */; };
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682928B6F1420069F315 /* ReactionResponse.swift */; };
7B81682C28B72F480069F315 /* PendingChange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B81682B28B72F480069F315 /* PendingChange.swift */; };
7B8D5FC428332600008324D9 /* VisibleMessage+Reaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */; };
7B93D06A27CF173D00811CB6 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D06927CF173D00811CB6 /* MessageRequestsViewController.swift */; };
7B93D07027CF194000811CB6 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B93D06E27CF194000811CB6 /* ConfigurationMessage+Convenience.swift */; };
@ -1178,6 +1180,8 @@
7B7CB191271508AD0079FF93 /* CallRingTonePlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallRingTonePlayer.swift; sourceTree = "<group>"; };
7B81682228A4C1210069F315 /* UpdateTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTypes.swift; sourceTree = "<group>"; };
7B81682728B310D50069F315 /* _007_HomeQueryOptimisationIndexes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _007_HomeQueryOptimisationIndexes.swift; sourceTree = "<group>"; };
7B81682928B6F1420069F315 /* ReactionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionResponse.swift; sourceTree = "<group>"; };
7B81682B28B72F480069F315 /* PendingChange.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PendingChange.swift; sourceTree = "<group>"; };
7B8D5FC328332600008324D9 /* VisibleMessage+Reaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VisibleMessage+Reaction.swift"; sourceTree = "<group>"; };
7B93D06927CF173D00811CB6 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
7B93D06E27CF194000811CB6 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
@ -3853,6 +3857,8 @@
FDC438A327BB107F00C60D73 /* UserBanRequest.swift */,
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */,
FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */,
7B81682928B6F1420069F315 /* ReactionResponse.swift */,
7B81682B28B72F480069F315 /* PendingChange.swift */,
);
path = Models;
sourceTree = "<group>";
@ -5190,6 +5196,7 @@
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */,
FDF40CDE2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift in Sources */,
FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */,
7B81682C28B72F480069F315 /* PendingChange.swift in Sources */,
FD77289A284AF1BD0018502F /* Sodium+Utilities.swift in Sources */,
FD5C7309285007920029977D /* BlindedIdLookup.swift in Sources */,
7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */,
@ -5203,6 +5210,7 @@
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */,
FD716E6A2850327900C96BF4 /* EndCallMode.swift in Sources */,
FDF0B75C2807F41D004C14C5 /* MessageSender+Convenience.swift in Sources */,
7B81682A28B6F1420069F315 /* ReactionResponse.swift in Sources */,
FD09799727FFA84A00936362 /* RecipientState.swift in Sources */,
FDA8EB00280E8D58002B68E5 /* FailedAttachmentDownloadsJob.swift in Sources */,
FD09798927FD1C5A00936362 /* OpenGroup.swift in Sources */,

@ -1137,6 +1137,14 @@ extension ConversationVC:
else { return }
if remove {
let pendingChange = OpenGroupManager
.addPendingReaction(
emoji: emoji,
id: openGroupServerMessageId,
in: openGroup.roomToken,
on: openGroup.server,
type: .remove
)
OpenGroupAPI
.reactionDelete(
db,
@ -1145,8 +1153,23 @@ extension ConversationVC:
in: openGroup.roomToken,
on: openGroup.server
)
.map { _, response in
OpenGroupManager
.updatePendingChange(
pendingChange,
seqNo: response.seqNo
)
}
.retainUntilComplete()
} else {
let pendingChange = OpenGroupManager
.addPendingReaction(
emoji: emoji,
id: openGroupServerMessageId,
in: openGroup.roomToken,
on: openGroup.server,
type: .react
)
OpenGroupAPI
.reactionAdd(
db,
@ -1155,6 +1178,13 @@ extension ConversationVC:
in: openGroup.roomToken,
on: openGroup.server
)
.map { _, response in
OpenGroupManager
.updatePendingChange(
pendingChange,
seqNo: response.seqNo
)
}
.retainUntilComplete()
}

@ -353,6 +353,7 @@ public extension Message {
_ db: Database,
openGroupId: String,
message: OpenGroupAPI.Message,
associatedPendingChanges: [OpenGroupAPI.PendingChange],
dependencies: SMKDependencies = SMKDependencies()
) -> [Reaction] {
var results: [Reaction] = []
@ -364,13 +365,46 @@ public extension Message {
threadVariant: .openGroup
)
for (encodedEmoji, rawReaction) in reactions {
if let emoji = encodedEmoji.removingPercentEncoding,
if let decodedEmoji = encodedEmoji.removingPercentEncoding,
rawReaction.count > 0,
let reactors = rawReaction.reactors
{
// Decide whether we need to add an extra reaction from current user
let pendingChangeSelfReaction: Bool? = {
// Find the newest 'PendingChange' entry with a matching emoji, if one exists, and
// set the "self reaction" value based on it's action
let maybePendingChange: OpenGroupAPI.PendingChange? = associatedPendingChanges
.sorted(by: { lhs, rhs -> Bool in (lhs.seqNo ?? Int64.max) > (rhs.seqNo ?? Int64.max) })
.first { pendingChange in
if case .reaction(_, let emoji, _) = pendingChange.metadata {
return emoji == decodedEmoji
}
return false
}
// If there is no pending change for this reaction then return nil
guard
let pendingChange: OpenGroupAPI.PendingChange = maybePendingChange,
case .reaction(_, _, let action) = pendingChange.metadata
else { return nil }
// Otherwise add/remove accordingly
return (action == .react)
}()
let shouldAddSelfReaction: Bool = (
pendingChangeSelfReaction ??
(rawReaction.you || reactors.contains(userPublicKey))
)
let count: Int64 = rawReaction.you ? rawReaction.count - 1 : rawReaction.count
let timestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000)))
let maxLength: Int = shouldAddSelfReaction ? 4 : 5
let desiredReactorIds: [String] = reactors
.filter { $0 != blindedUserPublicKey }
.filter { $0 != blindedUserPublicKey && $0 != userPublicKey } // Remove current user for now, will add back if needed
.prefix(maxLength)
.map{ $0 }
results = results
.appending( // Add the first reaction (with the count)
@ -381,8 +415,8 @@ public extension Message {
serverHash: nil,
timestampMs: timestampMs,
authorId: reactor,
emoji: emoji,
count: rawReaction.count,
emoji: decodedEmoji,
count: count,
sortId: rawReaction.index
)
}
@ -398,22 +432,22 @@ public extension Message {
serverHash: nil,
timestampMs: timestampMs,
authorId: reactor,
emoji: emoji,
emoji: decodedEmoji,
count: 0, // Only want this on the first reaction
sortId: rawReaction.index
)
}
)
.appending( // Add the current user reaction (if applicable and not already included)
!rawReaction.you || reactors.contains(userPublicKey) ?
!shouldAddSelfReaction ?
nil :
Reaction(
interactionId: message.id,
serverHash: nil,
timestampMs: timestampMs,
authorId: userPublicKey,
emoji: emoji,
count: (desiredReactorIds.isEmpty ? rawReaction.count : 0),
emoji: decodedEmoji,
count: 1,
sortId: rawReaction.index
)
)

@ -0,0 +1,41 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPI {
public struct PendingChange: Equatable {
enum ChangeType {
case reaction
}
enum Metadata {
case reaction(messageId: Int64, emoji: String, action: VisibleMessage.VMReaction.Kind)
}
let server: String
let room: String
let changeType: ChangeType
var seqNo: Int64?
let metadata: Metadata
public static func == (lhs: OpenGroupAPI.PendingChange, rhs: OpenGroupAPI.PendingChange) -> Bool {
guard lhs.server == rhs.server &&
lhs.room == rhs.room &&
lhs.changeType == rhs.changeType &&
lhs.seqNo == rhs.seqNo
else {
return false
}
switch lhs.changeType {
case .reaction:
if case .reaction(let lhsMessageId, let lhsEmoji, let lhsAction) = lhs.metadata,
case .reaction(let rhsMessageId, let rhsEmoji, let rhsAction) = rhs.metadata {
return lhsMessageId == rhsMessageId && lhsEmoji == rhsEmoji && lhsAction == rhsAction
} else {
return false
}
}
}
}
}

@ -0,0 +1,44 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
extension OpenGroupAPI {
public struct ReactionAddResponse: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case added
case seqNo = "seqno"
}
/// This field indicates whether the reaction was added (true) or already present (false).
public let added: Bool
/// The seqNo after the reaction is added.
public let seqNo: Int64
}
public struct ReactionRemoveResponse: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case removed
case seqNo = "seqno"
}
/// This field indicates whether the reaction was removed (true) or was not present to begin with (false).
public let removed: Bool
/// The seqNo after the reaction is removed.
public let seqNo: Int64
}
public struct ReactionRemoveAllResponse: Codable, Equatable {
enum CodingKeys: String, CodingKey {
case removed
case seqNo = "seqno"
}
/// This field shows the total number of reactions that were deleted.
public let removed: Int64
/// The seqNo after the reactions is all removed.
public let seqNo: Int64
}
}

@ -99,7 +99,7 @@ public enum OpenGroupAPI {
),
queryParameters: [
.updateTypes: UpdateTypes.reaction.rawValue,
.reactors: "20"
.reactors: "5"
]
),
responseType: [Failable<Message>].self
@ -701,7 +701,7 @@ public enum OpenGroupAPI {
in roomToken: String,
on server: String,
using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<OnionRequestResponseInfoType> {
) -> Promise<(OnionRequestResponseInfoType, ReactionAddResponse)> {
/// URL(String:) won't convert raw emojis, so need to do a little encoding here.
/// The raw emoji will come back when calling url.path
guard let encodedEmoji: String = emoji.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
@ -718,7 +718,7 @@ public enum OpenGroupAPI {
),
using: dependencies
)
.map { responseInfo, _ in responseInfo }
.decoded(as: ReactionAddResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
}
public static func reactionDelete(
@ -728,7 +728,7 @@ public enum OpenGroupAPI {
in roomToken: String,
on server: String,
using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<OnionRequestResponseInfoType> {
) -> Promise<(OnionRequestResponseInfoType, ReactionRemoveResponse)> {
/// URL(String:) won't convert raw emojis, so need to do a little encoding here.
/// The raw emoji will come back when calling url.path
guard let encodedEmoji: String = emoji.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
@ -745,7 +745,7 @@ public enum OpenGroupAPI {
),
using: dependencies
)
.map { responseInfo, _ in responseInfo }
.decoded(as: ReactionRemoveResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
}
public static func reactionDeleteAll(
@ -755,7 +755,7 @@ public enum OpenGroupAPI {
in roomToken: String,
on server: String,
using dependencies: SMKDependencies = SMKDependencies()
) -> Promise<OnionRequestResponseInfoType> {
) -> Promise<(OnionRequestResponseInfoType, ReactionRemoveAllResponse)> {
/// URL(String:) won't convert raw emojis, so need to do a little encoding here.
/// The raw emoji will come back when calling url.path
guard let encodedEmoji: String = emoji.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
@ -772,7 +772,7 @@ public enum OpenGroupAPI {
),
using: dependencies
)
.map { responseInfo, _ in responseInfo }
.decoded(as: ReactionRemoveAllResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
}
// MARK: - Pinning

@ -20,6 +20,8 @@ public protocol OGMCacheType {
var timeSinceLastPoll: [String: TimeInterval] { get set }
func getTimeSinceLastOpen(using dependencies: Dependencies) -> TimeInterval
var pendingChanges: [OpenGroupAPI.PendingChange] { get set }
}
// MARK: - OpenGroupManager
@ -53,6 +55,8 @@ public final class OpenGroupManager: NSObject {
_timeSinceLastOpen = dependencies.date.timeIntervalSince(lastOpen)
return dependencies.date.timeIntervalSince(lastOpen)
}
public var pendingChanges: [OpenGroupAPI.PendingChange] = []
}
// MARK: - Variables
@ -529,11 +533,17 @@ public final class OpenGroupManager: NSObject {
.filter { $0.deleted == true }
.map { $0.id }
// Update the 'openGroupSequenceNumber' value (Note: SOGS V4 uses the 'seqNo' instead of the 'serverId')
if let seqNo: Int64 = seqNo {
// Update the 'openGroupSequenceNumber' value (Note: SOGS V4 uses the 'seqNo' instead of the 'serverId')
_ = try? OpenGroup
.filter(id: openGroup.id)
.updateAll(db, OpenGroup.Columns.sequenceNumber.set(to: seqNo))
// Update pendingChange cache
dependencies.mutableCache.mutate {
$0.pendingChanges = $0.pendingChanges
.filter { $0.seqNo == nil || $0.seqNo! > seqNo }
}
}
// Process the messages
@ -589,11 +599,23 @@ public final class OpenGroupManager: NSObject {
db,
openGroupId: openGroup.id,
message: message,
associatedPendingChanges: dependencies.cache.pendingChanges
.filter {
guard $0.server == server && $0.room == roomToken && $0.changeType == .reaction else {
return false
}
if case .reaction(let messageId, _, _) = $0.metadata {
return messageId == message.id
}
return false
},
dependencies: dependencies
)
try MessageReceiver.handleOpenGroupReactions(
db,
threadId: openGroup.threadId,
openGroupMessageServerId: message.id,
openGroupReactions: reactions
)
@ -737,6 +759,44 @@ public final class OpenGroupManager: NSObject {
// MARK: - Convenience
public static func addPendingReaction(
emoji: String,
id: Int64,
in roomToken: String,
on server: String,
type: VisibleMessage.VMReaction.Kind,
using dependencies: OGMDependencies = OGMDependencies()
) -> OpenGroupAPI.PendingChange {
let pendingChange = OpenGroupAPI.PendingChange(
server: server,
room: roomToken,
changeType: .reaction,
metadata: .reaction(
messageId: id,
emoji: emoji,
action: type
)
)
dependencies.mutableCache.mutate {
$0.pendingChanges.append(pendingChange)
}
return pendingChange
}
public static func updatePendingChange(
_ pendingChange: OpenGroupAPI.PendingChange,
seqNo: Int64,
using dependencies: OGMDependencies = OGMDependencies()
) {
dependencies.mutableCache.mutate {
if let index = $0.pendingChanges.firstIndex(of: pendingChange) {
$0.pendingChanges[index].seqNo = seqNo
}
}
}
/// This method specifies if the given capability is supported on a specified Open Group
public static func isOpenGroupSupport(
_ capability: Capability.Variant,

@ -249,11 +249,13 @@ public enum MessageReceiver {
public static func handleOpenGroupReactions(
_ db: Database,
threadId: String,
openGroupMessageServerId: Int64,
openGroupReactions: [Reaction]
) throws {
guard let interactionId: Int64 = try? Interaction
.select(.id)
.filter(Interaction.Columns.threadId == threadId)
.filter(Interaction.Columns.openGroupServerMessageId == openGroupMessageServerId)
.asRequest(of: Int64.self)
.fetchOne(db)

Loading…
Cancel
Save