WIP: add sortId

This commit is contained in:
Ryan Zhao 2022-08-04 17:10:24 +10:00
parent 37f876dffd
commit a2c9bee269
9 changed files with 154 additions and 80 deletions

View File

@ -1086,13 +1086,19 @@ extension ConversationVC:
.deleteAll(db)
}
else {
let sortId = Reaction.getSortId(
db,
interactionId: cellViewModel.id,
emoji: emoji
)
try Reaction(
interactionId: cellViewModel.id,
serverHash: nil,
timestampMs: sentTimestamp,
authorId: cellViewModel.currentUserPublicKey,
emoji: emoji,
count: 1 // TODO: For open groups this should be '0'
count: 1,
sortId: sortId
).insert(db)
// Add it to the recent list

View File

@ -29,6 +29,9 @@ enum _005_EmojiReacts: Migration {
t.column(.count, .integer)
.notNull()
.defaults(to: 0)
t.column(.sortId, .integer)
.notNull()
.defaults(to: 0)
/// A specific author should only be able to have a single instance of each emoji on a particular interaction
t.uniqueKey([.interactionId, .emoji, .authorId])

View File

@ -19,6 +19,7 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
case authorId
case emoji
case count
case sortId
}
/// The id for the interaction this reaction belongs to
@ -45,6 +46,9 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
/// regardless of the type of conversation)
public let count: Int64
/// The id for sorting
public let sortId: Int64
// MARK: - Relationships
public var interaction: QueryInterfaceRequest<Interaction> {
@ -63,7 +67,8 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
timestampMs: Int64,
authorId: String,
emoji: String,
count: Int64
count: Int64,
sortId: Int64
) {
self.interactionId = interactionId
self.serverHash = serverHash
@ -71,6 +76,7 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
self.authorId = authorId
self.emoji = emoji
self.count = count
self.sortId = sortId
}
}
@ -81,7 +87,8 @@ public extension Reaction {
interactionId: Int64? = nil,
serverHash: String? = nil,
authorId: String? = nil,
count: Int64? = nil
count: Int64? = nil,
sortId: Int64? = nil
) -> Reaction {
return Reaction(
interactionId: (interactionId ?? self.interactionId),
@ -89,7 +96,41 @@ public extension Reaction {
timestampMs: self.timestampMs,
authorId: (authorId ?? self.authorId),
emoji: self.emoji,
count: (count ?? self.count)
count: (count ?? self.count),
sortId: (sortId ?? self.sortId)
)
}
}
// MARK: - SortId
public extension Reaction {
static func getSortId(
_ db: Database,
interactionId: Int64,
emoji: String
) -> Int64 {
let existingSortId: Int64? = try? Reaction
.select(Columns.sortId)
.filter(Columns.interactionId == interactionId)
.filter(Columns.emoji == emoji)
.asRequest(of: Int64.self)
.fetchOne(db)
if let sortId = existingSortId {
return sortId
}
let existingLargestSortId: Int64? = try? Reaction
.select(max(Columns.sortId))
.filter(Columns.interactionId == interactionId)
.asRequest(of: Int64.self)
.fetchOne(db)
if let sortId = existingLargestSortId {
return sortId + 1
}
return 0
}
}

View File

@ -106,36 +106,30 @@ extension MessageReceiveJob {
case message
case variant
case serializedProtoData
case reactions
}
public let message: Message
public let variant: Message.Variant
public let serializedProtoData: Data
public let reactions: [Reaction]
public init(
message: Message,
variant: Message.Variant,
proto: SNProtoContent,
reactions: [Reaction]
proto: SNProtoContent
) throws {
self.message = message
self.variant = variant
self.serializedProtoData = try proto.serializedData()
self.reactions = reactions
}
private init(
message: Message,
variant: Message.Variant,
serializedProtoData: Data,
reactions: [Reaction]
serializedProtoData: Data
) {
self.message = message
self.variant = variant
self.serializedProtoData = serializedProtoData
self.reactions = reactions
}
// MARK: - Codable
@ -151,8 +145,7 @@ extension MessageReceiveJob {
self = MessageInfo(
message: try variant.decode(from: container, forKey: .message),
variant: variant,
serializedProtoData: try container.decode(Data.self, forKey: .serializedProtoData),
reactions: try container.decode([Reaction].self, forKey: .reactions)
serializedProtoData: try container.decode(Data.self, forKey: .serializedProtoData)
)
}
@ -167,7 +160,6 @@ extension MessageReceiveJob {
try container.encode(message, forKey: .message)
try container.encode(variant, forKey: .variant)
try container.encode(serializedProtoData, forKey: .serializedProtoData)
try container.encode(reactions, forKey: .reactions)
}
}

View File

@ -303,21 +303,9 @@ public extension Message {
throw MessageReceiverError.invalidMessage
}
let reactions = processRawReceivedReactions(
db,
reactions: message.reactions,
serverExpirationTimestamp: nil,
serverHash: nil,
openGroupId: openGroupId,
openGroupMessageServerId: message.id,
openGroupServerPublicKey: openGroupServerPublicKey,
dependencies: dependencies
)
return try processRawReceivedMessage(
db,
envelope: envelope,
reactions: reactions,
serverExpirationTimestamp: nil,
serverHash: nil,
openGroupId: openGroupId,
@ -361,18 +349,14 @@ public extension Message {
)
}
private static func processRawReceivedReactions(
static func processRawReceivedReactions(
_ db: Database,
reactions: [String:OpenGroupAPI.Message.Reaction]?,
serverExpirationTimestamp: TimeInterval?,
serverHash: String?,
openGroupId: String,
openGroupMessageServerId: Int64? = nil,
openGroupServerPublicKey: String? = nil,
message: OpenGroupAPI.Message,
dependencies: SMKDependencies = SMKDependencies()
) -> [Reaction] {
var results: [Reaction] = []
guard let openGroupMessageServerId = openGroupMessageServerId, let reactions = reactions else { return results }
guard let reactions = message.reactions else { return results }
let userPublicKey: String = getUserHexEncodedPublicKey(db)
let blindedUserPublicKey: String? = SessionThread
.getUserHexEncodedBlindedKey(
@ -385,27 +369,30 @@ public extension Message {
let reactors = rawReaction.reactors
{
var count = rawReaction.count
let sortId: Int64 = 0 // TODO: Need to be modified to the server returned value
for reactor in reactors {
if reactor == blindedUserPublicKey { continue } // Will add a reaction for this case outside of the loop
let reaction = Reaction(
interactionId: openGroupMessageServerId,
interactionId: message.id,
serverHash: nil,
timestampMs: Int64(floor((Date().timeIntervalSince1970 * 1000))),
authorId: reactor,
emoji: emoji,
count: count
count: count,
sortId: sortId
)
count = 0 // Only insert the first reaction with the total count of this emoji
results.append(reaction)
}
if rawReaction.you && !reactors.contains(userPublicKey) {
let reaction = Reaction(
interactionId: openGroupMessageServerId,
interactionId: message.id,
serverHash: nil,
timestampMs: Int64(floor((Date().timeIntervalSince1970 * 1000))),
authorId: userPublicKey,
emoji: emoji,
count: 0
count: count,
sortId: sortId
)
results.append(reaction)
}
@ -417,7 +404,6 @@ public extension Message {
private static func processRawReceivedMessage(
_ db: Database,
envelope: SNProtoEnvelope,
reactions: [Reaction] = [],
serverExpirationTimestamp: TimeInterval?,
serverHash: String?,
openGroupId: String? = nil,
@ -487,8 +473,7 @@ public extension Message {
try MessageReceiveJob.Details.MessageInfo(
message: message,
variant: variant,
proto: proto,
reactions: reactions
proto: proto
)
)
}

View File

@ -94,7 +94,7 @@ extension OpenGroupAPI.Message {
self = OpenGroupAPI.Message(
id: try container.decode(Int64.self, forKey: .id),
sender: try? container.decode(String.self, forKey: .sender),
posted: try container.decode(TimeInterval.self, forKey: .posted),
posted: ((try? container.decode(TimeInterval.self, forKey: .posted)) ?? Date().timeIntervalSince1970), // Reaction updates don't include posted
edited: try? container.decode(TimeInterval.self, forKey: .edited),
seqNo: try container.decode(Int64.self, forKey: .seqNo),
whisper: ((try? container.decode(Bool.self, forKey: .whisper)) ?? false),

View File

@ -512,48 +512,72 @@ public final class OpenGroupManager: NSObject {
// Process the messages
sortedMessages.forEach { message in
guard
let base64EncodedString: String = message.base64EncodedData,
let data = Data(base64Encoded: base64EncodedString)
else {
if message.base64EncodedData == nil && message.reactions == nil {
// A message with no data has been deleted so add it to the list to remove
messageServerIdsToRemove.append(UInt64(message.id))
return
}
do {
let processedMessage: ProcessedMessage? = try Message.processReceivedOpenGroupMessage(
db,
openGroupId: openGroup.id,
openGroupServerPublicKey: openGroup.publicKey,
message: message,
data: data,
dependencies: dependencies
)
if let messageInfo: MessageReceiveJob.Details.MessageInfo = processedMessage?.messageInfo {
try MessageReceiver.handle(
// Handle messages
if let base64EncodedString: String = message.base64EncodedData,
let data = Data(base64Encoded: base64EncodedString)
{
do {
let processedMessage: ProcessedMessage? = try Message.processReceivedOpenGroupMessage(
db,
message: messageInfo.message,
associatedWithProto: try SNProtoContent.parseData(messageInfo.serializedProtoData),
openGroupId: openGroup.id,
openGroupReactions: messageInfo.reactions,
isBackgroundPoll: isBackgroundPoll,
openGroupServerPublicKey: openGroup.publicKey,
message: message,
data: data,
dependencies: dependencies
)
if let messageInfo: MessageReceiveJob.Details.MessageInfo = processedMessage?.messageInfo {
try MessageReceiver.handle(
db,
message: messageInfo.message,
associatedWithProto: try SNProtoContent.parseData(messageInfo.serializedProtoData),
openGroupId: openGroup.id,
isBackgroundPoll: isBackgroundPoll,
dependencies: dependencies
)
}
}
catch {
switch error {
// Ignore duplicate & selfSend message errors (and don't bother logging
// them as there will be a lot since we each service node duplicates messages)
case DatabaseError.SQLITE_CONSTRAINT_UNIQUE,
MessageReceiverError.duplicateMessage,
MessageReceiverError.duplicateControlMessage,
MessageReceiverError.selfSend:
break
default: SNLog("Couldn't receive open group message due to error: \(error).")
}
}
}
catch {
switch error {
// Ignore duplicate & selfSend message errors (and don't bother logging
// them as there will be a lot since we each service node duplicates messages)
case DatabaseError.SQLITE_CONSTRAINT_UNIQUE,
MessageReceiverError.duplicateMessage,
MessageReceiverError.duplicateControlMessage,
MessageReceiverError.selfSend:
break
// Handle reactions
if message.reactions != nil {
do {
let reactions: [Reaction] = Message.processRawReceivedReactions(
db,
openGroupId: openGroup.id,
message: message,
dependencies: dependencies
)
default: SNLog("Couldn't receive open group message due to error: \(error).")
if !reactions.isEmpty {
try MessageReceiver.handleOpenGroupReactions(
db,
openGroupMessageServerId: message.id,
openGroupReactions: reactions
)
}
}
catch {
SNLog("Couldn't handle open group reactions due to error: \(error).")
}
}
}

View File

@ -12,7 +12,6 @@ extension MessageReceiver {
message: VisibleMessage,
associatedWithProto proto: SNProtoContent,
openGroupId: String?,
openGroupReactions: [Reaction] = [],
isBackgroundPoll: Bool,
dependencies: Dependencies = Dependencies()
) throws -> Int64 {
@ -141,10 +140,6 @@ extension MessageReceiver {
return recipientParts[2]
}()
).inserted(db)
for reaction in openGroupReactions {
try reaction.with(interactionId: interaction.id).insert(db)
}
}
catch {
switch error {
@ -329,6 +324,12 @@ extension MessageReceiver {
throw StorageError.objectNotFound
}
let sortId = Reaction.getSortId(
db,
interactionId: interactionId,
emoji: reaction.emoji
)
switch reaction.kind {
case .react:
try Reaction(
@ -337,7 +338,8 @@ extension MessageReceiver {
timestampMs: Int64(messageSentTimestamp * 1000),
authorId: sender,
emoji: reaction.emoji,
count: 1 // TODO: Handle Open Group case
count: 1,
sortId: sortId
).insert(db)
case .remove:

View File

@ -180,7 +180,6 @@ public enum MessageReceiver {
message: Message,
associatedWithProto proto: SNProtoContent,
openGroupId: String?,
openGroupReactions: [Reaction] = [],
isBackgroundPoll: Bool,
dependencies: SMKDependencies = SMKDependencies()
) throws {
@ -218,7 +217,6 @@ public enum MessageReceiver {
message: message,
associatedWithProto: proto,
openGroupId: openGroupId,
openGroupReactions: openGroupReactions,
isBackgroundPoll: isBackgroundPoll
)
@ -251,6 +249,29 @@ public enum MessageReceiver {
}
}
public static func handleOpenGroupReactions(
_ db: Database,
openGroupMessageServerId: Int64,
openGroupReactions: [Reaction]
) throws {
guard let interactionId: Int64 = try? Interaction
.select(.id)
.filter(Interaction.Columns.openGroupServerMessageId == openGroupMessageServerId)
.asRequest(of: Int64.self)
.fetchOne(db)
else {
throw MessageReceiverError.invalidMessage
}
_ = try Reaction
.filter(Reaction.Columns.interactionId == interactionId)
.deleteAll(db)
for reaction in openGroupReactions {
try reaction.with(interactionId: interactionId).insert(db)
}
}
// MARK: - Convenience
internal static func threadInfo(_ db: Database, message: Message, openGroupId: String?) -> (id: String, variant: SessionThread.Variant)? {