WIP: add sortId

pull/638/head
Ryan Zhao 3 years ago
parent 37f876dffd
commit a2c9bee269

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

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

@ -19,6 +19,7 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
case authorId case authorId
case emoji case emoji
case count case count
case sortId
} }
/// The id for the interaction this reaction belongs to /// 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) /// regardless of the type of conversation)
public let count: Int64 public let count: Int64
/// The id for sorting
public let sortId: Int64
// MARK: - Relationships // MARK: - Relationships
public var interaction: QueryInterfaceRequest<Interaction> { public var interaction: QueryInterfaceRequest<Interaction> {
@ -63,7 +67,8 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
timestampMs: Int64, timestampMs: Int64,
authorId: String, authorId: String,
emoji: String, emoji: String,
count: Int64 count: Int64,
sortId: Int64
) { ) {
self.interactionId = interactionId self.interactionId = interactionId
self.serverHash = serverHash self.serverHash = serverHash
@ -71,6 +76,7 @@ public struct Reaction: Codable, Equatable, Hashable, FetchableRecord, Persistab
self.authorId = authorId self.authorId = authorId
self.emoji = emoji self.emoji = emoji
self.count = count self.count = count
self.sortId = sortId
} }
} }
@ -81,7 +87,8 @@ public extension Reaction {
interactionId: Int64? = nil, interactionId: Int64? = nil,
serverHash: String? = nil, serverHash: String? = nil,
authorId: String? = nil, authorId: String? = nil,
count: Int64? = nil count: Int64? = nil,
sortId: Int64? = nil
) -> Reaction { ) -> Reaction {
return Reaction( return Reaction(
interactionId: (interactionId ?? self.interactionId), interactionId: (interactionId ?? self.interactionId),
@ -89,7 +96,41 @@ public extension Reaction {
timestampMs: self.timestampMs, timestampMs: self.timestampMs,
authorId: (authorId ?? self.authorId), authorId: (authorId ?? self.authorId),
emoji: self.emoji, 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
}
}

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

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

@ -94,7 +94,7 @@ extension OpenGroupAPI.Message {
self = OpenGroupAPI.Message( self = OpenGroupAPI.Message(
id: try container.decode(Int64.self, forKey: .id), id: try container.decode(Int64.self, forKey: .id),
sender: try? container.decode(String.self, forKey: .sender), 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), edited: try? container.decode(TimeInterval.self, forKey: .edited),
seqNo: try container.decode(Int64.self, forKey: .seqNo), seqNo: try container.decode(Int64.self, forKey: .seqNo),
whisper: ((try? container.decode(Bool.self, forKey: .whisper)) ?? false), whisper: ((try? container.decode(Bool.self, forKey: .whisper)) ?? false),

@ -512,15 +512,16 @@ public final class OpenGroupManager: NSObject {
// Process the messages // Process the messages
sortedMessages.forEach { message in sortedMessages.forEach { message in
guard if message.base64EncodedData == nil && message.reactions == nil {
let base64EncodedString: String = message.base64EncodedData,
let data = Data(base64Encoded: base64EncodedString)
else {
// A message with no data has been deleted so add it to the list to remove // A message with no data has been deleted so add it to the list to remove
messageServerIdsToRemove.append(UInt64(message.id)) messageServerIdsToRemove.append(UInt64(message.id))
return return
} }
// Handle messages
if let base64EncodedString: String = message.base64EncodedData,
let data = Data(base64Encoded: base64EncodedString)
{
do { do {
let processedMessage: ProcessedMessage? = try Message.processReceivedOpenGroupMessage( let processedMessage: ProcessedMessage? = try Message.processReceivedOpenGroupMessage(
db, db,
@ -537,7 +538,6 @@ public final class OpenGroupManager: NSObject {
message: messageInfo.message, message: messageInfo.message,
associatedWithProto: try SNProtoContent.parseData(messageInfo.serializedProtoData), associatedWithProto: try SNProtoContent.parseData(messageInfo.serializedProtoData),
openGroupId: openGroup.id, openGroupId: openGroup.id,
openGroupReactions: messageInfo.reactions,
isBackgroundPoll: isBackgroundPoll, isBackgroundPoll: isBackgroundPoll,
dependencies: dependencies dependencies: dependencies
) )
@ -558,6 +558,30 @@ public final class OpenGroupManager: NSObject {
} }
} }
// Handle reactions
if message.reactions != nil {
do {
let reactions: [Reaction] = Message.processRawReceivedReactions(
db,
openGroupId: openGroup.id,
message: message,
dependencies: dependencies
)
if !reactions.isEmpty {
try MessageReceiver.handleOpenGroupReactions(
db,
openGroupMessageServerId: message.id,
openGroupReactions: reactions
)
}
}
catch {
SNLog("Couldn't handle open group reactions due to error: \(error).")
}
}
}
// Handle any deletions that are needed // Handle any deletions that are needed
guard !messageServerIdsToRemove.isEmpty else { return } guard !messageServerIdsToRemove.isEmpty else { return }

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

@ -180,7 +180,6 @@ public enum MessageReceiver {
message: Message, message: Message,
associatedWithProto proto: SNProtoContent, associatedWithProto proto: SNProtoContent,
openGroupId: String?, openGroupId: String?,
openGroupReactions: [Reaction] = [],
isBackgroundPoll: Bool, isBackgroundPoll: Bool,
dependencies: SMKDependencies = SMKDependencies() dependencies: SMKDependencies = SMKDependencies()
) throws { ) throws {
@ -218,7 +217,6 @@ public enum MessageReceiver {
message: message, message: message,
associatedWithProto: proto, associatedWithProto: proto,
openGroupId: openGroupId, openGroupId: openGroupId,
openGroupReactions: openGroupReactions,
isBackgroundPoll: isBackgroundPoll 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 // MARK: - Convenience
internal static func threadInfo(_ db: Database, message: Message, openGroupId: String?) -> (id: String, variant: SessionThread.Variant)? { internal static func threadInfo(_ db: Database, message: Message, openGroupId: String?) -> (id: String, variant: SessionThread.Variant)? {

Loading…
Cancel
Save