Skip to content

Commit

Permalink
Delete message with WorkManager
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyuehyang committed May 20, 2022
1 parent f678b94 commit 20fcd9b
Show file tree
Hide file tree
Showing 21 changed files with 661 additions and 46 deletions.
8 changes: 4 additions & 4 deletions Mixin/Service/Audio/Playlist/PlaylistManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ class PlaylistManager: NSObject {
name: MessageDAO.didInsertMessageNotification,
object: nil)
notificationCenter.addObserver(self,
selector: #selector(messageDAOWillDeleteMessage(_:)),
name: MessageDAO.willDeleteMessageNotification,
selector: #selector(messageWillDelete(_:)),
name: DeleteMessageWork.willDeleteNotification,
object: nil)
notificationCenter.addObserver(self,
selector: #selector(conversationDAOWillClearConversation(_:)),
Expand Down Expand Up @@ -773,8 +773,8 @@ extension PlaylistManager {
loadLaterItemsIfNeeded()
}

@objc private func messageDAOWillDeleteMessage(_ notification: Notification) {
guard let messageId = notification.userInfo?[MessageDAO.UserInfoKey.messageId] as? String else {
@objc private func messageWillDelete(_ notification: Notification) {
guard let messageId = notification.userInfo?[DeleteMessageWork.messageIdUserInfoKey] as? String else {
return
}
guard case .conversation = source else {
Expand Down
3 changes: 2 additions & 1 deletion Mixin/Service/Job/AttachmentUploadJob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ class AttachmentUploadJob: AttachmentLoadingJob {
return false
}
guard let fileUrl = fileUrl else {
MessageDAO.shared.deleteMessage(id: messageId)
let work = DeleteMessageWork(message: message)
WorkManager.general.addWork(work)
return false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2683,10 +2683,8 @@ extension ConversationViewController {
guard let weakSelf = self, let indexPath = weakSelf.dataSource.indexPath(where: { $0.messageId == message.messageId }) else {
return
}
let (deleted, childMessageIds) = MessageDAO.shared.deleteMessage(id: message.messageId)
if deleted {
ReceiveMessageService.shared.stopRecallMessage(item: message, childMessageIds: childMessageIds)
}
let work = DeleteMessageWork(message: message)
WorkManager.general.addWork(work)
DispatchQueue.main.sync {
_ = weakSelf.dataSource?.removeViewModel(at: indexPath)
weakSelf.tableView.reloadData()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class DatabaseUpgradeViewController: UIViewController {
AppGroupContainer.migrateIfNeeded()
TaskDatabase.reloadCurrent()
UserDatabase.reloadCurrent()
WorkDatabase.reloadCurrent()

if !AppGroupUserDefaults.Database.isSentSenderKeyCleared {
UserDatabase.current.clearSentSenderKey()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ class HomeViewController: UIViewController {
if AppGroupUserDefaults.User.hasRecoverMedia {
ConcurrentJobQueue.shared.addJob(job: RecoverMediaJob())
}
WorkManager.general.wakeUpPersistedWorks(with: [DeleteMessageWork.self])
initializeFTSIfNeeded()
refreshExternalSchemesIfNeeded()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ class LoginVerificationCodeViewController: VerificationCodeViewController {

TaskDatabase.reloadCurrent()
UserDatabase.reloadCurrent()
WorkDatabase.reloadCurrent()
if AppGroupUserDefaults.User.isLogoutByServer {
UserDatabase.current.clearSentSenderKey()
AppGroupUserDefaults.Database.isSentSenderKeyCleared = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,18 +90,16 @@ public final class ExpiredMessageDAO: UserDatabaseDAO {
.limit(100)
.fetchAll(db)
let expiredMessages = try MessageDAO.shared.getFullMessages(messageIds: expiredMessageIds)
for id in expiredMessageIds {
let (deleted, childMessageIds) = try MessageDAO.shared.deleteMessage(id: id, with: db)
for message in expiredMessages {
let (deleted, childMessageIds) = try MessageDAO.shared.deleteMessage(message, with: db)
if deleted {
if let message = expiredMessages.first(where: { $0.messageId == id }) {
ReceiveMessageService.shared.stopRecallMessage(item: message, childMessageIds: childMessageIds)
if message.status != MessageStatus.READ.rawValue {
try MessageDAO.shared.updateUnseenMessageCount(database: db, conversationId: message.conversationId)
}
ReceiveMessageService.shared.stopRecallMessage(item: message, childMessageIds: childMessageIds)
if message.status != MessageStatus.READ.rawValue {
try MessageDAO.shared.updateUnseenMessageCount(database: db, conversationId: message.conversationId)
}
NotificationCenter.default.post(onMainThread: Self.expiredMessageDidDeleteNotification,
object: nil,
userInfo: [Self.messageIdKey: id])
userInfo: [Self.messageIdKey: message.messageId])
}
}
if !expiredMessageIds.isEmpty {
Expand Down
59 changes: 32 additions & 27 deletions MixinServices/MixinServices/Database/User/DAO/MessageDAO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public final class MessageDAO: UserDatabaseDAO {

public static let shared = MessageDAO()

public static let willDeleteMessageNotification = Notification.Name("one.mixin.services.MessageDAO.willDeleteMessage")
public static let didInsertMessageNotification = Notification.Name("one.mixin.services.did.insert.msg")
public static let didRedecryptMessageNotification = Notification.Name("one.mixin.services.did.redecrypt.msg")
public static let messageMediaStatusDidUpdateNotification = Notification.Name("one.mixin.services.MessageDAO.MessageMediaStatusDidUpdate")
Expand Down Expand Up @@ -764,47 +763,53 @@ public final class MessageDAO: UserDatabaseDAO {
}
}

@discardableResult
public func deleteMessage(id: String) -> (deleted: Bool, childMessageIds: [String]) {
NotificationCenter.default.post(onMainThread: Self.willDeleteMessageNotification,
object: self,
userInfo: [UserInfoKey.messageId: id])
var deleted = false
var childMessageIds: [String] = []
db.write { (db) in
(deleted, childMessageIds) = try deleteMessage(id: id, with: db)
}
return (deleted, childMessageIds)
}

func deleteMessage(id: String, with database: GRDB.Database) throws -> (deleted: Bool, childMessageIds: [String]) {
var deleteCount = 0
var childMessageIds: [String] = []
let conversationId: String? = try Message
.select(Message.column(of: .conversationId))
.filter(Message.column(of: .messageId) == id)
.fetchOne(database)
deleteCount = try Message
func deleteMessage(_ message: MessageItem, with database: GRDB.Database) throws -> (deleted: Bool, childMessageIds: [String]) {
let id = message.messageId
let deleteCount = try Message
.filter(Message.column(of: .messageId) == id)
.deleteAll(database)
try MessageMention
.filter(MessageMention.column(of: .messageId) == id)
.deleteAll(database)
try deleteFTSContent(database, messageId: id)
childMessageIds = try TranscriptMessage
let childMessageIds: [String] = try TranscriptMessage
.select(TranscriptMessage.column(of: .messageId))
.filter(TranscriptMessage.column(of: .transcriptId) == id)
.fetchAll(database)
try TranscriptMessage
.filter(TranscriptMessage.column(of: .transcriptId) == id)
.deleteAll(database)
if let conversationId = conversationId {
try PinMessageDAO.shared.delete(messageIds: [id], conversationId: conversationId, from: database)
try clearPinMessageContent(quoteMessageIds: [id], conversationId: conversationId, from: database)
}
try PinMessageDAO.shared.delete(messageIds: [id], conversationId: message.conversationId, from: database)
try clearPinMessageContent(quoteMessageIds: [id], conversationId: message.conversationId, from: database)
return (deleteCount > 0, childMessageIds)
}

public func delete(id: String, conversationId: String, completion: @escaping () -> Void) {
db.write { db in
try Message
.filter(Message.column(of: .messageId) == id)
.deleteAll(db)
try MessageMention
.filter(MessageMention.column(of: .messageId) == id)
.deleteAll(db)
try deleteFTSContent(db, messageId: id)
try PinMessageDAO.shared.delete(messageIds: [id], conversationId: conversationId, from: db)
try clearPinMessageContent(quoteMessageIds: [id], conversationId: conversationId, from: db)
db.afterNextTransactionCommit { _ in
completion()
}
}
}

public func deleteLegacyMessage(with id: String) {
db.write { db in
try Message
.filter(Message.column(of: .messageId) == id)
.deleteAll(db)
try deleteFTSContent(db, messageId: id)
}
}

public func hasSentMessage(inConversationOf conversationId: String) -> Bool {
let possibleStatus = [
MessageStatus.SENDING.rawValue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,9 @@ public final class TranscriptMessageDAO: UserDatabaseDAO {
}
}

public func deleteTranscriptMessages(with transcriptId: String) {
db.delete(TranscriptMessage.self,
where: TranscriptMessage.column(of: .transcriptId) == transcriptId)
}

}
35 changes: 35 additions & 0 deletions MixinServices/MixinServices/Database/Work/WorkDAO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Foundation
import GRDB

public class WorkDAO {

public static let shared = WorkDAO()

public var db: Database {
WorkDatabase.current
}

public func save(work: PersistedWork, completion: @escaping () -> Void) {
db.write { db in
try work.save(db)
db.afterNextTransactionCommit { _ in
completion()
}
}
}

public func works(with types: [String]) -> [PersistedWork] {
db.select(where: types.contains(PersistedWork.column(of: .type)))
}

public func delete(id: String) {
db.delete(PersistedWork.self, where: PersistedWork.column(of: .id) == id)
}

public func update(context: Data?, forWorkWith id: String) {
db.update(PersistedWork.self,
assignments: [PersistedWork.column(of: .context).set(to: context)],
where: PersistedWork.column(of: .id) == id)
}

}
44 changes: 44 additions & 0 deletions MixinServices/MixinServices/Database/Work/WorkDatabase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import GRDB

public final class WorkDatabase: Database {

public private(set) static var current: WorkDatabase! = try! WorkDatabase(url: AppGroupContainer.workDatabaseURL)

public override class var config: Configuration {
var config = super.config
config.label = "Work"
return config
}

public override var needsMigration: Bool {
try! pool.read({ (db) -> Bool in
let migrationsCompleted = try migrator.hasCompletedMigrations(db)
return !migrationsCompleted
})
}

private var migrator: DatabaseMigrator {
var migrator = DatabaseMigrator()

migrator.registerMigration("create_table") { db in
try db.create(table: "works") { td in
td.column("id", .text).primaryKey().notNull()
td.column("type", .text).notNull()
td.column("context", .blob)
td.column("priority", .integer).notNull()
}
}

return migrator
}

public static func reloadCurrent() {
current = try! WorkDatabase(url: AppGroupContainer.workDatabaseURL)
current.migrate()
}

private func migrate() {
try! migrator.migrate(pool)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public enum AppGroupContainer {
accountUrl.appendingPathComponent("task.db", isDirectory: false)
}

public static var workDatabaseURL: URL {
accountUrl.appendingPathComponent("work.db", isDirectory: false)
}

@available(iOSApplicationExtension, unavailable)
public static func migrateIfNeeded() {
guard !AppGroupUserDefaults.isDocumentsMigrated else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,25 @@ public enum AttachmentContainer {
}
let url = AttachmentContainer.url(for: category, filename: mediaUrl)
try? FileManager.default.removeItem(at: url)
Logger.general.debug(category: "AttachmentContainer", message: "\(url) deleted")
if category == .videos {
let thumbUrl = AttachmentContainer.videoThumbnailURL(videoFilename: mediaUrl)
try? FileManager.default.removeItem(at: thumbUrl)
Logger.general.debug(category: "AttachmentContainer", message: "\(thumbUrl) deleted")
}
}

public static func removeAll(transcriptId: String) {
let url = Self.url(transcriptId: transcriptId, filename: nil)
try? FileManager.default.removeItem(at: url)
Logger.general.debug(category: "AttachmentContainer", message: "\(url) deleted")
}

}

public extension AttachmentContainer {

enum Category: CaseIterable {
enum Category: String, CaseIterable, Codable {

case audios
case files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ extension AppGroupUserDefaults {
|| TaskDatabase.current.needsMigration
|| SignalDatabase.current.needsMigration
|| UserDatabase.current.needsMigration
|| WorkDatabase.current.needsMigration
}

@Default(namespace: .user, key: Key.localVersion, defaultValue: uninitializedVersion)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ open class AttachmentLoadingJob: AsynchronousJob {
return
}
if weakSelf.isCancelled || !LoginManager.shared.isLoggedIn {
Logger.general.debug(category: "AttachmentLoadingJob", message: "\(weakSelf.jobId) is cancelled")
weakSelf.finishJob()
return
} else if let error = error {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public class WebSocketService {
case let .failure(error):
if case .invalidRequestData = error {
if let param = message.params, let messageId = param.messageId, messageId != messageId.lowercased() {
MessageDAO.shared.deleteMessage(id: messageId)
MessageDAO.shared.deleteLegacyMessage(with: messageId)
JobDAO.shared.removeJob(jobId: message.id)
}
}
Expand Down
Loading

0 comments on commit 20fcd9b

Please sign in to comment.