WIP: make group leaving a job
This commit is contained in:
parent
eed8b1dfcb
commit
cea2e1522d
|
@ -451,11 +451,12 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
|
||||||
Storage.shared
|
Storage.shared
|
||||||
.writeAsync { db in
|
.writeAsync { db in
|
||||||
if !updatedMemberIds.contains(userPublicKey) {
|
if !updatedMemberIds.contains(userPublicKey) {
|
||||||
return try MessageSender
|
try MessageSender.leave(
|
||||||
.leave(db, groupPublicKey: threadId)
|
db,
|
||||||
.map { (_, error) in
|
groupPublicKey: threadId,
|
||||||
if let error: Error = error { throw error }
|
deleteThread: false
|
||||||
}
|
)
|
||||||
|
return Promise.value(())
|
||||||
}
|
}
|
||||||
|
|
||||||
return try MessageSender.update(
|
return try MessageSender.update(
|
||||||
|
|
|
@ -405,7 +405,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
||||||
),
|
),
|
||||||
onTap: { [weak self] in
|
onTap: { [weak self] in
|
||||||
dependencies.storage.writeAsync { db in
|
dependencies.storage.writeAsync { db in
|
||||||
try MessageSender.leave(db, groupPublicKey: threadId)
|
try MessageSender.leave(db, groupPublicKey: threadId, deleteThread: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -305,28 +305,11 @@ public class HomeViewModel {
|
||||||
Storage.shared.writeAsync { db in
|
Storage.shared.writeAsync { db in
|
||||||
switch threadVariant {
|
switch threadVariant {
|
||||||
case .closedGroup:
|
case .closedGroup:
|
||||||
try MessageSender
|
try MessageSender.leave(
|
||||||
.leave(db, groupPublicKey: threadId)
|
db,
|
||||||
.done { (interactionId, error) in
|
groupPublicKey: threadId,
|
||||||
Storage.shared.writeAsync { db in
|
deleteThread: true
|
||||||
if let _ = error {
|
)
|
||||||
try Interaction
|
|
||||||
.filter(id: interactionId)
|
|
||||||
.updateAll(
|
|
||||||
db,
|
|
||||||
[
|
|
||||||
Interaction.Columns.variant.set(to: Interaction.Variant.infoClosedGroupCurrentUserErrorLeaving),
|
|
||||||
Interaction.Columns.body.set(to: "group_unable_to_leave".localized())
|
|
||||||
]
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_ = try SessionThread
|
|
||||||
.filter(id: threadId)
|
|
||||||
.deleteAll(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.retainUntilComplete()
|
|
||||||
|
|
||||||
case .openGroup:
|
case .openGroup:
|
||||||
OpenGroupManager.shared.delete(db, openGroupId: threadId)
|
OpenGroupManager.shared.delete(db, openGroupId: threadId)
|
||||||
|
|
|
@ -46,5 +46,6 @@ public enum SNMessagingKit { // Just to make the external API nice
|
||||||
JobRunner.add(executor: SendReadReceiptsJob.self, for: .sendReadReceipts)
|
JobRunner.add(executor: SendReadReceiptsJob.self, for: .sendReadReceipts)
|
||||||
JobRunner.add(executor: AttachmentDownloadJob.self, for: .attachmentDownload)
|
JobRunner.add(executor: AttachmentDownloadJob.self, for: .attachmentDownload)
|
||||||
JobRunner.add(executor: AttachmentUploadJob.self, for: .attachmentUpload)
|
JobRunner.add(executor: AttachmentUploadJob.self, for: .attachmentUpload)
|
||||||
|
JobRunner.add(executor: GroupLeavingJob.self, for: .groupLeaving)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
{
|
{
|
||||||
guard
|
guard
|
||||||
let detailsData: Data = job.details,
|
let detailsData: Data = job.details,
|
||||||
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData)
|
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData),
|
||||||
|
let interactionId: Int64 = job.interactionId
|
||||||
else {
|
else {
|
||||||
failure(job, JobRunnerError.missingRequiredDetails, false)
|
failure(job, JobRunnerError.missingRequiredDetails, false)
|
||||||
return
|
return
|
||||||
|
@ -44,7 +45,7 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
message: ClosedGroupControlMessage(
|
message: ClosedGroupControlMessage(
|
||||||
kind: .memberLeft
|
kind: .memberLeft
|
||||||
),
|
),
|
||||||
interactionId: details.infoMessageInteractionId,
|
interactionId: interactionId,
|
||||||
in: thread
|
in: thread
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
)
|
)
|
||||||
|
|
||||||
try Interaction
|
try Interaction
|
||||||
.filter(id: details.infoMessageInteractionId)
|
.filter(id: interactionId)
|
||||||
.updateAll(
|
.updateAll(
|
||||||
db,
|
db,
|
||||||
[
|
[
|
||||||
|
@ -94,7 +95,7 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
.deleteAll(db)
|
.deleteAll(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
if details.deleteThreadAfterSuccess {
|
if details.deleteThread {
|
||||||
_ = try SessionThread
|
_ = try SessionThread
|
||||||
.filter(id: thread.id)
|
.filter(id: thread.id)
|
||||||
.deleteAll(db)
|
.deleteAll(db)
|
||||||
|
@ -105,7 +106,7 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
.catch(on: queue) { error in
|
.catch(on: queue) { error in
|
||||||
Storage.shared.write { db in
|
Storage.shared.write { db in
|
||||||
try Interaction
|
try Interaction
|
||||||
.filter(id: details.infoMessageInteractionId)
|
.filter(id: job.interactionId)
|
||||||
.updateAll(
|
.updateAll(
|
||||||
db,
|
db,
|
||||||
[
|
[
|
||||||
|
@ -125,25 +126,21 @@ public enum GroupLeavingJob: JobExecutor {
|
||||||
extension GroupLeavingJob {
|
extension GroupLeavingJob {
|
||||||
public struct Details: Codable {
|
public struct Details: Codable {
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case infoMessageInteractionId
|
|
||||||
case groupPublicKey
|
case groupPublicKey
|
||||||
case deleteThreadAfterSuccess
|
case deleteThread
|
||||||
}
|
}
|
||||||
|
|
||||||
public let infoMessageInteractionId: Int64
|
|
||||||
public let groupPublicKey: String
|
public let groupPublicKey: String
|
||||||
public let deleteThreadAfterSuccess: Bool
|
public let deleteThread: Bool
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
infoMessageInteractionId: Int64,
|
|
||||||
groupPublicKey: String,
|
groupPublicKey: String,
|
||||||
deleteThreadAfterSuccess: Bool
|
deleteThread: Bool
|
||||||
) {
|
) {
|
||||||
self.infoMessageInteractionId = infoMessageInteractionId
|
|
||||||
self.groupPublicKey = groupPublicKey
|
self.groupPublicKey = groupPublicKey
|
||||||
self.deleteThreadAfterSuccess = deleteThreadAfterSuccess
|
self.deleteThread = deleteThread
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Codable
|
// MARK: - Codable
|
||||||
|
@ -152,18 +149,16 @@ extension GroupLeavingJob {
|
||||||
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
let container: KeyedDecodingContainer<CodingKeys> = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
self = Details(
|
self = Details(
|
||||||
infoMessageInteractionId: try container.decode(Int64.self, forKey: .infoMessageInteractionId),
|
|
||||||
groupPublicKey: try container.decode(String.self, forKey: .groupPublicKey),
|
groupPublicKey: try container.decode(String.self, forKey: .groupPublicKey),
|
||||||
deleteThreadAfterSuccess: try container.decode(Bool.self, forKey: .deleteThreadAfterSuccess)
|
deleteThread: try container.decode(Bool.self, forKey: .deleteThread)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
var container: KeyedEncodingContainer<CodingKeys> = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
try container.encode(infoMessageInteractionId, forKey: .infoMessageInteractionId)
|
|
||||||
try container.encode(groupPublicKey, forKey: .groupPublicKey)
|
try container.encode(groupPublicKey, forKey: .groupPublicKey)
|
||||||
try container.encode(deleteThreadAfterSuccess, forKey: .deleteThreadAfterSuccess)
|
try container.encode(deleteThread, forKey: .deleteThread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -478,13 +478,9 @@ extension MessageSender {
|
||||||
/// unregisters from push notifications.
|
/// unregisters from push notifications.
|
||||||
///
|
///
|
||||||
/// The returned promise is fulfilled when the `MEMBER_LEFT` message has been sent to the group.
|
/// The returned promise is fulfilled when the `MEMBER_LEFT` message has been sent to the group.
|
||||||
public static func leave(_ db: Database, groupPublicKey: String) throws -> Promise<(Int64, Error?)> {
|
public static func leave(_ db: Database, groupPublicKey: String, deleteThread: Bool) throws {
|
||||||
guard let thread: SessionThread = try? SessionThread.fetchOne(db, id: groupPublicKey) else {
|
guard let thread: SessionThread = try? SessionThread.fetchOne(db, id: groupPublicKey) else {
|
||||||
SNLog("Can't leave nonexistent closed group.")
|
return
|
||||||
return Promise(error: MessageSenderError.noThread)
|
|
||||||
}
|
|
||||||
guard let closedGroup: ClosedGroup = try? thread.closedGroup.fetchOne(db) else {
|
|
||||||
return Promise(error: MessageSenderError.invalidClosedGroupUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||||
|
@ -498,78 +494,18 @@ extension MessageSender {
|
||||||
timestampMs: SnodeAPI.currentOffsetTimestampMs()
|
timestampMs: SnodeAPI.currentOffsetTimestampMs()
|
||||||
).inserted(db)
|
).inserted(db)
|
||||||
|
|
||||||
guard let interactionId: Int64 = interaction.id else {
|
JobRunner.add(
|
||||||
throw StorageError.objectNotSaved
|
db,
|
||||||
}
|
job: Job(
|
||||||
|
variant: .groupLeaving,
|
||||||
// Send the update to the group
|
threadId: thread.id,
|
||||||
let (promise, seal) = Promise<(Int64, Error?)>.pending()
|
interactionId: interaction.id,
|
||||||
do {
|
details: GroupLeavingJob.Details(
|
||||||
try MessageSender
|
groupPublicKey: groupPublicKey,
|
||||||
.sendNonDurably(
|
deleteThread: deleteThread
|
||||||
db,
|
|
||||||
message: ClosedGroupControlMessage(
|
|
||||||
kind: .memberLeft
|
|
||||||
),
|
|
||||||
interactionId: interactionId,
|
|
||||||
in: thread
|
|
||||||
)
|
)
|
||||||
.done {
|
)
|
||||||
// Remove the group from the database and unsubscribe from PNs
|
)
|
||||||
ClosedGroupPoller.shared.stopPolling(for: groupPublicKey)
|
|
||||||
|
|
||||||
Storage.shared.write { db in
|
|
||||||
try closedGroup
|
|
||||||
.keyPairs
|
|
||||||
.deleteAll(db)
|
|
||||||
|
|
||||||
let _ = PushNotificationAPI.performOperation(
|
|
||||||
.unsubscribe,
|
|
||||||
for: groupPublicKey,
|
|
||||||
publicKey: userPublicKey
|
|
||||||
)
|
|
||||||
|
|
||||||
try Interaction
|
|
||||||
.filter(id: interactionId)
|
|
||||||
.updateAll(
|
|
||||||
db,
|
|
||||||
[
|
|
||||||
Interaction.Columns.variant.set(to: Interaction.Variant.infoClosedGroupCurrentUserLeft),
|
|
||||||
Interaction.Columns.body.set(to: "GROUP_YOU_LEFT".localized())
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update the group (if the admin leaves the group is disbanded)
|
|
||||||
let wasAdminUser: Bool = try GroupMember
|
|
||||||
.filter(GroupMember.Columns.groupId == thread.id)
|
|
||||||
.filter(GroupMember.Columns.profileId == userPublicKey)
|
|
||||||
.filter(GroupMember.Columns.role == GroupMember.Role.admin)
|
|
||||||
.isNotEmpty(db)
|
|
||||||
|
|
||||||
if wasAdminUser {
|
|
||||||
try GroupMember
|
|
||||||
.filter(GroupMember.Columns.groupId == thread.id)
|
|
||||||
.deleteAll(db)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
try GroupMember
|
|
||||||
.filter(GroupMember.Columns.groupId == thread.id)
|
|
||||||
.filter(GroupMember.Columns.profileId == userPublicKey)
|
|
||||||
.deleteAll(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
seal.fulfill((interactionId, nil))
|
|
||||||
}
|
|
||||||
.catch { error in
|
|
||||||
seal.fulfill((interactionId, error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
seal.fulfill((interactionId, error))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return
|
|
||||||
return promise
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -763,26 +763,3 @@ public final class MessageSender {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Objective-C Support
|
|
||||||
|
|
||||||
// FIXME: Remove when possible
|
|
||||||
|
|
||||||
@objc(SMKMessageSender)
|
|
||||||
public class SMKMessageSender: NSObject {
|
|
||||||
@objc(leaveClosedGroupWithPublicKey:)
|
|
||||||
public static func objc_leave(_ groupPublicKey: String) -> AnyPromise {
|
|
||||||
let promise = Storage.shared.writeAsync { db in
|
|
||||||
try MessageSender.leave(db, groupPublicKey: groupPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return AnyPromise.from(promise)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(forceSyncConfigurationNow)
|
|
||||||
public static func objc_forceSyncConfigurationNow() {
|
|
||||||
Storage.shared.write { db in
|
|
||||||
try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ public final class JobRunner {
|
||||||
jobVariants.remove(.attachmentUpload),
|
jobVariants.remove(.attachmentUpload),
|
||||||
jobVariants.remove(.messageSend),
|
jobVariants.remove(.messageSend),
|
||||||
jobVariants.remove(.notifyPushServer),
|
jobVariants.remove(.notifyPushServer),
|
||||||
jobVariants.remove(.sendReadReceipts)
|
jobVariants.remove(.sendReadReceipts),
|
||||||
|
jobVariants.remove(.groupLeaving)
|
||||||
].compactMap { $0 }
|
].compactMap { $0 }
|
||||||
)
|
)
|
||||||
let messageReceiveQueue: JobQueue = JobQueue(
|
let messageReceiveQueue: JobQueue = JobQueue(
|
||||||
|
|
Loading…
Reference in New Issue