feat: add local cache and deal with merging for reaction changes

pull/676/head
Ryan Zhao 3 years ago
parent 3a81ffc752
commit 543f729247

@ -139,6 +139,7 @@
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 */; };
@ -1180,6 +1181,7 @@
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>"; };
@ -3856,6 +3858,7 @@
FDC438A527BB113A00C60D73 /* UserUnbanRequest.swift */,
FDC438A927BB12BB00C60D73 /* UserModeratorRequest.swift */,
7B81682928B6F1420069F315 /* ReactionResponse.swift */,
7B81682B28B72F480069F315 /* PendingChange.swift */,
);
path = Models;
sourceTree = "<group>";
@ -5193,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 */,

@ -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,14 +365,33 @@ 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 pendingChanges = associatedPendingChanges
.filter {
if case .reaction(_, let emoji, _) = $0.metadata {
return emoji == decodedEmoji
}
return false
}
var shouldAddSelfReaction: Bool = rawReaction.you || reactors.contains(userPublicKey)
pendingChanges.forEach {
if case .reaction(_, _, let action) = $0.metadata {
switch action {
case .react: shouldAddSelfReaction = true
case .remove: shouldAddSelfReaction = false
}
}
}
let count: Int64 = shouldAddSelfReaction ? rawReaction.count - 1 : rawReaction.count
let timestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000)))
let maxLength: Int = !rawReaction.you || reactors.contains(userPublicKey) ? 5 : 4
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 }
@ -384,8 +404,8 @@ public extension Message {
serverHash: nil,
timestampMs: timestampMs,
authorId: reactor,
emoji: emoji,
count: rawReaction.count,
emoji: decodedEmoji,
count: count,
sortId: rawReaction.index
)
}
@ -401,22 +421,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
}
}
}
}
}

@ -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 {
@ -719,10 +719,6 @@ public enum OpenGroupAPI {
using: dependencies
)
.decoded(as: ReactionAddResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
.map { responseInfo, addResponse in
print("\(addResponse)")
return responseInfo
}
}
public static func reactionDelete(
@ -732,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 {
@ -750,10 +746,6 @@ public enum OpenGroupAPI {
using: dependencies
)
.decoded(as: ReactionRemoveResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
.map { responseInfo, removeResponse in
print("\(removeResponse)")
return responseInfo
}
}
public static func reactionDeleteAll(
@ -763,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 {
@ -781,10 +773,6 @@ public enum OpenGroupAPI {
using: dependencies
)
.decoded(as: ReactionRemoveAllResponse.self, on: OpenGroupAPI.workQueue, using: dependencies)
.map { responseInfo, removeAllResponse in
print("\(removeAllResponse)")
return responseInfo
}
}
// 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,6 +599,17 @@ 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
)
@ -738,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,

Loading…
Cancel
Save