conversations-classic-ios/ConversationsClassic/AppCore/Middlewares/DatabaseMiddleware.swift
2024-06-24 15:28:26 +02:00

165 lines
6.9 KiB
Swift

import Combine
import Foundation
import GRDB
final class DatabaseMiddleware {
static let shared = DatabaseMiddleware()
private let database = Database.shared
private var cancellables: Set<AnyCancellable> = []
private init() {
// Database changes
ValueObservation
.tracking(Roster.fetchAll)
.publisher(in: database._db, scheduling: .immediate)
.sink { _ in
// Handle completion
} receiveValue: { rosters in
DispatchQueue.main.async {
store.dispatch(.databaseAction(.storedRostersLoaded(rosters: rosters)))
}
}
.store(in: &cancellables)
ValueObservation
.tracking(Chat.fetchAll)
.publisher(in: database._db, scheduling: .immediate)
.sink { _ in
// Handle completion
} receiveValue: { chats in
DispatchQueue.main.async {
store.dispatch(.databaseAction(.storedChatsLoaded(chats: chats)))
}
}
.store(in: &cancellables)
}
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
switch action {
case .startAction(.loadStoredAccounts):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
return
}
do {
try database._db.read { db in
let accounts = try Account.fetchAll(db)
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
}
} catch {
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
}
}
}
.eraseToAnyPublisher()
case .accountsAction(.makeAccountPermanent(let account)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.databaseAction(.updateAccountFailed)))
return
}
do {
try database._db.write { db in
// make permanent and store to database
var acc = account
acc.isTemp = false
try acc.insert(db)
// Re-Fetch all accounts
let accounts = try Account.fetchAll(db)
// Use the accounts
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
}
} catch {
promise(.success(.databaseAction(.updateAccountFailed)))
}
}
}
.eraseToAnyPublisher()
case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
_ = try database._db.write { db in
try Roster
.filter(Column("bareJid") == ownerJID)
.filter(Column("contactBareJid") == contactJID)
.updateAll(db, Column("locallyDeleted").set(to: true))
}
promise(.success(.empty))
} catch {
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
}
}
}
.eraseToAnyPublisher()
case .rostersAction(.unmarkRosterAsLocallyDeleted(let ownerJID, let contactJID)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
_ = try database._db.write { db in
try Roster
.filter(Column("bareJid") == ownerJID)
.filter(Column("contactBareJid") == contactJID)
.updateAll(db, Column("locallyDeleted").set(to: false))
}
promise(.success(.empty))
} catch {
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
}
}
}
.eraseToAnyPublisher()
case .chatsAction(.createNewChat(let accountJid, let participantJid)):
return Future<AppAction, Never> { promise in
Task(priority: .background) { [weak self] in
guard let database = self?.database else {
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
return
}
do {
try database._db.write { db in
let chat = Chat(
id: UUID().uuidString,
account: accountJid,
participant: participantJid,
type: .chat
)
try chat.insert(db)
promise(.success(.chatsAction(.chatCreated(chat: chat))))
}
} catch {
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
}
}
}
.eraseToAnyPublisher()
case .xmppAction(.xmppMessageReceived(let message)):
if message.type != .chat {
return Empty().eraseToAnyPublisher()
}
// TODO: Store msg here!
return Empty().eraseToAnyPublisher()
default:
return Empty().eraseToAnyPublisher()
}
}
}