mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
WIP: add sortId
This commit is contained in:
parent
37f876dffd
commit
a2c9bee269
9 changed files with 154 additions and 80 deletions
|
@ -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
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -512,15 +512,16 @@ 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
|
||||
}
|
||||
|
||||
// Handle messages
|
||||
if let base64EncodedString: String = message.base64EncodedData,
|
||||
let data = Data(base64Encoded: base64EncodedString)
|
||||
{
|
||||
do {
|
||||
let processedMessage: ProcessedMessage? = try Message.processReceivedOpenGroupMessage(
|
||||
db,
|
||||
|
@ -537,7 +538,6 @@ public final class OpenGroupManager: NSObject {
|
|||
message: messageInfo.message,
|
||||
associatedWithProto: try SNProtoContent.parseData(messageInfo.serializedProtoData),
|
||||
openGroupId: openGroup.id,
|
||||
openGroupReactions: messageInfo.reactions,
|
||||
isBackgroundPoll: isBackgroundPoll,
|
||||
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
|
||||
guard !messageServerIdsToRemove.isEmpty else { return }
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)? {
|
||||
|
|
Loading…
Reference in a new issue