Fixed a couple of config handling bugs

Fixed an bug where config messages could be processed in the wrong order
Tweaked the behaviour or removing threads (this would cause issues with future config-based settings changes that live on the thread getting lost)
This commit is contained in:
Morgan Pretty 2023-09-01 16:16:13 +10:00
parent 6d990559b7
commit 7a8941db5c
7 changed files with 68 additions and 57 deletions

View File

@ -574,7 +574,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
!SessionUtil.conversationInConfig( !SessionUtil.conversationInConfig(
threadId: threadId, threadId: threadId,
threadVariant: viewModel.threadData.threadVariant, threadVariant: viewModel.threadData.threadVariant,
visibleOnly: true visibleOnly: false
) )
{ {
Storage.shared.writeAsync { db in Storage.shared.writeAsync { db in

View File

@ -164,7 +164,7 @@ public extension UIContextualAction {
db, db,
threadId: threadViewModel.threadId, threadId: threadViewModel.threadId,
threadVariant: threadViewModel.threadVariant, threadVariant: threadViewModel.threadVariant,
groupLeaveType: .forced, groupLeaveType: .silent,
calledFromConfigHandling: false calledFromConfigHandling: false
) )
} }

View File

@ -296,17 +296,18 @@ public extension SessionThread {
calledFromConfigHandling: Bool calledFromConfigHandling: Bool
) throws { ) throws {
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db) let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
let remainingThreadIds: [String] = threadIds.filter { $0 != currentUserPublicKey } let remainingThreadIds: Set<String> = threadIds.asSet().removing(currentUserPublicKey)
switch (threadVariant, groupLeaveType) { switch (threadVariant, groupLeaveType) {
case (.contact, _): case (.contact, .standard), (.contact, .silent):
// Clear any interactions for the deleted thread
_ = try Interaction
.filter(threadIds.contains(Interaction.Columns.threadId))
.deleteAll(db)
// We need to custom handle the 'Note to Self' conversation (it should just be // We need to custom handle the 'Note to Self' conversation (it should just be
// hidden rather than deleted // hidden locally rather than deleted)
if threadIds.contains(currentUserPublicKey) { if threadIds.contains(currentUserPublicKey) {
_ = try Interaction
.filter(Interaction.Columns.threadId == currentUserPublicKey)
.deleteAll(db)
_ = try SessionThread _ = try SessionThread
.filter(id: currentUserPublicKey) .filter(id: currentUserPublicKey)
.updateAllAndConfig( .updateAllAndConfig(
@ -314,17 +315,28 @@ public extension SessionThread {
SessionThread.Columns.pinnedPriority.set(to: 0), SessionThread.Columns.pinnedPriority.set(to: 0),
SessionThread.Columns.shouldBeVisible.set(to: false) SessionThread.Columns.shouldBeVisible.set(to: false)
) )
return
} }
// Update any other threads to be hidden (don't want to actually delete the thread
// record in case it's settings get changed while it's not visible)
_ = try SessionThread
.filter(id: remainingThreadIds)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfig,
SessionThread.Columns.pinnedPriority.set(to: SessionUtil.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
)
case (.contact, .forced):
// If this wasn't called from config handling then we need to hide the conversation // If this wasn't called from config handling then we need to hide the conversation
if !calledFromConfigHandling { if !calledFromConfigHandling {
try SessionUtil try SessionUtil
.hide(db, contactIds: threadIds) .remove(db, contactIds: remainingThreadIds)
} }
_ = try SessionThread _ = try SessionThread
.filter(ids: remainingThreadIds) .filter(id: remainingThreadIds)
.deleteAll(db) .deleteAll(db)
case (.legacyGroup, .standard), (.group, .standard): case (.legacyGroup, .standard), (.group, .standard):

View File

@ -138,46 +138,37 @@ internal extension SessionUtil {
let threadExists: Bool = (threadInfo != nil) let threadExists: Bool = (threadInfo != nil)
let updatedShouldBeVisible: Bool = SessionUtil.shouldBeVisible(priority: data.priority) let updatedShouldBeVisible: Bool = SessionUtil.shouldBeVisible(priority: data.priority)
switch (updatedShouldBeVisible, threadExists) { /// If we are hiding the conversation then kick the user from it if it's currently open
case (false, true): if !updatedShouldBeVisible {
SessionUtil.kickFromConversationUIIfNeeded(removedThreadIds: [sessionId]) SessionUtil.kickFromConversationUIIfNeeded(removedThreadIds: [sessionId])
}
try SessionThread
.deleteOrLeave( /// Create the thread if it doesn't exist, otherwise just update it's state
db, if !threadExists {
threadId: sessionId, try SessionThread(
threadVariant: .contact, id: sessionId,
groupLeaveType: .forced, variant: .contact,
calledFromConfigHandling: true creationDateTimestamp: data.created,
) shouldBeVisible: updatedShouldBeVisible,
pinnedPriority: data.priority
case (true, false): ).save(db)
try SessionThread( }
id: sessionId, else {
variant: .contact, let changes: [ConfigColumnAssignment] = [
creationDateTimestamp: data.created, (threadInfo?.shouldBeVisible == updatedShouldBeVisible ? nil :
shouldBeVisible: true, SessionThread.Columns.shouldBeVisible.set(to: updatedShouldBeVisible)
pinnedPriority: data.priority ),
).save(db) (threadInfo?.pinnedPriority == data.priority ? nil :
SessionThread.Columns.pinnedPriority.set(to: data.priority)
case (true, true): )
let changes: [ConfigColumnAssignment] = [ ].compactMap { $0 }
(threadInfo?.shouldBeVisible == updatedShouldBeVisible ? nil :
SessionThread.Columns.shouldBeVisible.set(to: updatedShouldBeVisible) try SessionThread
), .filter(id: sessionId)
(threadInfo?.pinnedPriority == data.priority ? nil : .updateAll( // Handling a config update so don't use `updateAllAndConfig`
SessionThread.Columns.pinnedPriority.set(to: data.priority) db,
) changes
].compactMap { $0 } )
try SessionThread
.filter(id: sessionId)
.updateAll( // Handling a config update so don't use `updateAllAndConfig`
db,
changes
)
case (false, false): break
} }
} }

View File

@ -113,7 +113,7 @@ internal extension SessionUtil {
db, db,
threadId: userPublicKey, threadId: userPublicKey,
threadVariant: .contact, threadVariant: .contact,
groupLeaveType: .forced, groupLeaveType: .silent,
calledFromConfigHandling: true calledFromConfigHandling: true
) )
} }

View File

@ -52,14 +52,16 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
@discardableResult @discardableResult
func updateAllAndConfig( func updateAllAndConfig(
_ db: Database, _ db: Database,
calledFromConfig: Bool = false,
_ assignments: ConfigColumnAssignment... _ assignments: ConfigColumnAssignment...
) throws -> Int { ) throws -> Int {
return try updateAllAndConfig(db, assignments) return try updateAllAndConfig(db, calledFromConfig: calledFromConfig, assignments)
} }
@discardableResult @discardableResult
func updateAllAndConfig( func updateAllAndConfig(
_ db: Database, _ db: Database,
calledFromConfig: Bool = false,
_ assignments: [ConfigColumnAssignment] _ assignments: [ConfigColumnAssignment]
) throws -> Int { ) throws -> Int {
let targetAssignments: [ColumnAssignment] = assignments.map { $0.assignment } let targetAssignments: [ColumnAssignment] = assignments.map { $0.assignment }
@ -69,7 +71,7 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
return try self.updateAll(db, targetAssignments) return try self.updateAll(db, targetAssignments)
} }
return try self.updateAndFetchAllAndUpdateConfig(db, assignments).count return try self.updateAndFetchAllAndUpdateConfig(db, calledFromConfig: calledFromConfig, assignments).count
} }
// MARK: -- updateAndFetchAll // MARK: -- updateAndFetchAll
@ -77,21 +79,26 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
@discardableResult @discardableResult
func updateAndFetchAllAndUpdateConfig( func updateAndFetchAllAndUpdateConfig(
_ db: Database, _ db: Database,
calledFromConfig: Bool = false,
_ assignments: ConfigColumnAssignment... _ assignments: ConfigColumnAssignment...
) throws -> [RowDecoder] { ) throws -> [RowDecoder] {
return try updateAndFetchAllAndUpdateConfig(db, assignments) return try updateAndFetchAllAndUpdateConfig(db, calledFromConfig: calledFromConfig, assignments)
} }
@discardableResult @discardableResult
func updateAndFetchAllAndUpdateConfig( func updateAndFetchAllAndUpdateConfig(
_ db: Database, _ db: Database,
calledFromConfig: Bool = false,
_ assignments: [ConfigColumnAssignment] _ assignments: [ConfigColumnAssignment]
) throws -> [RowDecoder] { ) throws -> [RowDecoder] {
// First perform the actual updates // First perform the actual updates
let updatedData: [RowDecoder] = try self.updateAndFetchAll(db, assignments.map { $0.assignment }) let updatedData: [RowDecoder] = try self.updateAndFetchAll(db, assignments.map { $0.assignment })
// Then check if any of the changes could affect the config // Then check if any of the changes could affect the config
guard SessionUtil.assignmentsRequireConfigUpdate(assignments) else { return updatedData } guard
!calledFromConfig &&
SessionUtil.assignmentsRequireConfigUpdate(assignments)
else { return updatedData }
defer { defer {
// If we changed a column that requires a config update then we may as well automatically // If we changed a column that requires a config update then we may as well automatically

View File

@ -360,6 +360,7 @@ public enum SessionUtil {
guard !publicKey.isEmpty else { throw MessageReceiverError.noThread } guard !publicKey.isEmpty else { throw MessageReceiverError.noThread }
let groupedMessages: [ConfigDump.Variant: [SharedConfigMessage]] = messages let groupedMessages: [ConfigDump.Variant: [SharedConfigMessage]] = messages
.sorted { lhs, rhs in lhs.seqNo < rhs.seqNo }
.grouped(by: \.kind.configDumpVariant) .grouped(by: \.kind.configDumpVariant)
let needsPush: Bool = try groupedMessages let needsPush: Bool = try groupedMessages