fix performance
This commit is contained in:
parent
794c50fed0
commit
889211683b
|
@ -13,6 +13,7 @@ final class Store<State: Stateable, Action: Codable>: ObservableObject {
|
||||||
|
|
||||||
// Serial queue for performing any actions sequentially
|
// Serial queue for performing any actions sequentially
|
||||||
private let serialQueue = DispatchQueue(label: "im.narayana.conversations.classic.serial.queue", qos: .userInteractive)
|
private let serialQueue = DispatchQueue(label: "im.narayana.conversations.classic.serial.queue", qos: .userInteractive)
|
||||||
|
private let middlewareQueue = DispatchQueue(label: "im.narayana.conversations.classic.middleware.queue", qos: .default, attributes: .concurrent)
|
||||||
|
|
||||||
private let reducer: Reducer<State, Action>
|
private let reducer: Reducer<State, Action>
|
||||||
private let middlewares: [Middleware<State, Action>]
|
private let middlewares: [Middleware<State, Action>]
|
||||||
|
@ -71,8 +72,11 @@ final class Store<State: Stateable, Action: Codable>: ObservableObject {
|
||||||
}
|
}
|
||||||
startTime = CFAbsoluteTimeGetCurrent()
|
startTime = CFAbsoluteTimeGetCurrent()
|
||||||
middleware
|
middleware
|
||||||
|
.subscribe(on: middlewareQueue)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink(receiveValue: dispatch)
|
.sink(receiveValue: { [weak self] action in
|
||||||
|
self?.dispatch(action)
|
||||||
|
})
|
||||||
.store(in: &middlewareCancellables)
|
.store(in: &middlewareCancellables)
|
||||||
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
|
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
|
||||||
if timeElapsed > 0.05 {
|
if timeElapsed > 0.05 {
|
||||||
|
|
|
@ -22,39 +22,43 @@ final class AccountsMiddleware {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.clientConnectionChanged(let jid, let connectionStatus)):
|
case .xmppAction(.clientConnectionChanged(let jid, let connectionStatus)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
guard let account = state.accountsState.accounts.first(where: { $0.bareJid == jid }) else {
|
Future<AppAction, Never> { promise in
|
||||||
promise(.success(.info("AccountsMiddleware: account not found for jid \(jid)")))
|
guard let account = state.accountsState.accounts.first(where: { $0.bareJid == jid }) else {
|
||||||
return
|
promise(.success(.info("AccountsMiddleware: account not found for jid \(jid)")))
|
||||||
}
|
return
|
||||||
if account.isTemp {
|
}
|
||||||
switch connectionStatus {
|
if account.isTemp {
|
||||||
case .connected:
|
switch connectionStatus {
|
||||||
promise(.success(.accountsAction(.makeAccountPermanent(account: account))))
|
case .connected:
|
||||||
|
promise(.success(.accountsAction(.makeAccountPermanent(account: account))))
|
||||||
case .disconnected(let reason):
|
|
||||||
if reason != "No error!" {
|
case .disconnected(let reason):
|
||||||
promise(.success(.accountsAction(.addAccountError(jid: jid, reason: reason))))
|
if reason != "No error!" {
|
||||||
} else {
|
promise(.success(.accountsAction(.addAccountError(jid: jid, reason: reason))))
|
||||||
promise(.success(.info("AccountsMiddleware: account \(jid) disconnected with no error")))
|
} else {
|
||||||
}
|
promise(.success(.info("AccountsMiddleware: account \(jid) disconnected with no error")))
|
||||||
|
}
|
||||||
default:
|
|
||||||
promise(.success(.info("AccountsMiddleware: account \(jid) connection status changed to \(connectionStatus)")))
|
default:
|
||||||
|
promise(.success(.info("AccountsMiddleware: account \(jid) connection status changed to \(connectionStatus)")))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
promise(.success(.info("AccountsMiddleware: account \(jid) is not temporary, ignoring")))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
promise(.success(.info("AccountsMiddleware: account \(jid) is not temporary, ignoring")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.serverFeaturesLoaded(let jid, let features)):
|
case .xmppAction(.serverFeaturesLoaded(let jid, let features)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
let serverFeatures = features
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
.compactMap { featureId in
|
let serverFeatures = features
|
||||||
self?.allFeatures.first(where: { $0.xmppId == featureId })
|
.compactMap { featureId in
|
||||||
}
|
self?.allFeatures.first(where: { $0.xmppId == featureId })
|
||||||
promise(.success(.accountsAction(.clientServerFeaturesUpdated(jid: jid, features: serverFeatures))))
|
}
|
||||||
|
promise(.success(.accountsAction(.clientServerFeaturesUpdated(jid: jid, features: serverFeatures))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,15 @@ final class ChatsMiddleware {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .chatsAction(.startChat(accountJid: let accountJid, participantJid: let participantJid)):
|
case .chatsAction(.startChat(accountJid: let accountJid, participantJid: let participantJid)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
if let exist = state.chatsState.chats.first(where: { $0.account == accountJid && $0.participant == participantJid }) {
|
Future<AppAction, Never> { promise in
|
||||||
// open existing chat
|
if let exist = state.chatsState.chats.first(where: { $0.account == accountJid && $0.participant == participantJid }) {
|
||||||
promise(.success(.chatsAction(.chatStarted(chat: exist))))
|
// open existing chat
|
||||||
} else {
|
promise(.success(.chatsAction(.chatStarted(chat: exist))))
|
||||||
// create new chat
|
} else {
|
||||||
promise(.success(.chatsAction(.createNewChat(accountJid: accountJid, participantJid: participantJid))))
|
// create new chat
|
||||||
|
promise(.success(.chatsAction(.createNewChat(accountJid: accountJid, participantJid: participantJid))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
|
@ -6,10 +6,12 @@ final class ConversationMiddleware {
|
||||||
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
switch action {
|
switch action {
|
||||||
case .chatsAction(.chatStarted(let chat)):
|
case .chatsAction(.chatStarted(let chat)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
let roster = state.rostersState.rosters
|
Future<AppAction, Never> { promise in
|
||||||
.first { $0.bareJid == chat.account && $0.contactBareJid == chat.participant }
|
let roster = state.rostersState.rosters
|
||||||
promise(.success(.conversationAction(.makeConversationActive(chat: chat, roster: roster))))
|
.first { $0.bareJid == chat.account && $0.contactBareJid == chat.participant }
|
||||||
|
promise(.success(.conversationAction(.makeConversationActive(chat: chat, roster: roster))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
|
|
@ -40,46 +40,50 @@ final class DatabaseMiddleware {
|
||||||
switch action {
|
switch action {
|
||||||
// MARK: Accounts
|
// MARK: Accounts
|
||||||
case .startAction(.loadStoredAccounts):
|
case .startAction(.loadStoredAccounts):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
try database._db.read { db in
|
do {
|
||||||
let accounts = try Account.fetchAll(db)
|
try database._db.read { db in
|
||||||
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
let accounts = try Account.fetchAll(db)
|
||||||
|
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .accountsAction(.makeAccountPermanent(let account)):
|
case .accountsAction(.makeAccountPermanent(let account)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAccountFailed)))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.updateAccountFailed)))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
try database._db.write { db in
|
do {
|
||||||
// make permanent and store to database
|
try database._db.write { db in
|
||||||
var acc = account
|
// make permanent and store to database
|
||||||
acc.isTemp = false
|
var acc = account
|
||||||
try acc.insert(db)
|
acc.isTemp = false
|
||||||
|
try acc.insert(db)
|
||||||
// Re-Fetch all accounts
|
|
||||||
let accounts = try Account.fetchAll(db)
|
// Re-Fetch all accounts
|
||||||
|
let accounts = try Account.fetchAll(db)
|
||||||
// Use the accounts
|
|
||||||
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
// Use the accounts
|
||||||
|
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAccountFailed)))
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAccountFailed)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,44 +91,48 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
// MARK: Rosters
|
// MARK: Rosters
|
||||||
case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Roster
|
_ = try database._db.write { db in
|
||||||
.filter(Column("bareJid") == ownerJID)
|
try Roster
|
||||||
.filter(Column("contactBareJid") == contactJID)
|
.filter(Column("bareJid") == ownerJID)
|
||||||
.updateAll(db, Column("locallyDeleted").set(to: true))
|
.filter(Column("contactBareJid") == contactJID)
|
||||||
|
.updateAll(db, Column("locallyDeleted").set(to: true))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) marked as locally deleted")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) marked as locally deleted")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .rostersAction(.unmarkRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
case .rostersAction(.unmarkRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Roster
|
_ = try database._db.write { db in
|
||||||
.filter(Column("bareJid") == ownerJID)
|
try Roster
|
||||||
.filter(Column("contactBareJid") == contactJID)
|
.filter(Column("bareJid") == ownerJID)
|
||||||
.updateAll(db, Column("locallyDeleted").set(to: false))
|
.filter(Column("contactBareJid") == contactJID)
|
||||||
|
.updateAll(db, Column("locallyDeleted").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) unmarked as locally deleted")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: roster \(contactJID) for account \(ownerJID) unmarked as locally deleted")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,25 +140,27 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
// MARK: Chats
|
// MARK: Chats
|
||||||
case .chatsAction(.createNewChat(let accountJid, let participantJid)):
|
case .chatsAction(.createNewChat(let accountJid, let participantJid)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
try database._db.write { db in
|
do {
|
||||||
let chat = Chat(
|
try database._db.write { db in
|
||||||
id: UUID().uuidString,
|
let chat = Chat(
|
||||||
account: accountJid,
|
id: UUID().uuidString,
|
||||||
participant: participantJid,
|
account: accountJid,
|
||||||
type: .chat
|
participant: participantJid,
|
||||||
)
|
type: .chat
|
||||||
try chat.insert(db)
|
)
|
||||||
promise(.success(.chatsAction(.chatCreated(chat: chat))))
|
try chat.insert(db)
|
||||||
|
promise(.success(.chatsAction(.chatCreated(chat: chat))))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
promise(.success(.chatsAction(.chatCreationFailed(reason: L10n.Global.Error.genericDbError))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,56 +172,60 @@ final class DatabaseMiddleware {
|
||||||
return Empty().eraseToAnyPublisher()
|
return Empty().eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppMessageReceived(let message)):
|
case .xmppAction(.xmppMessageReceived(let message)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
guard message.contentType != .typing, message.body != nil else {
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(message.id) received as 'typing...' or message body is nil")))
|
guard message.contentType != .typing, message.body != nil else {
|
||||||
return
|
promise(.success(.info("DatabaseMiddleware: message \(message.id) received as 'typing...' or message body is nil")))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
try database._db.write { db in
|
do {
|
||||||
try message.insert(db)
|
try database._db.write { db in
|
||||||
|
try message.insert(db)
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(message.id) stored in db")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(message.id) stored in db")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .conversationAction(.sendMessage(let from, let to, let body)):
|
case .conversationAction(.sendMessage(let from, let to, let body)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
let message = Message(
|
do {
|
||||||
id: UUID().uuidString,
|
let message = Message(
|
||||||
type: .chat,
|
id: UUID().uuidString,
|
||||||
contentType: .text,
|
type: .chat,
|
||||||
from: from,
|
contentType: .text,
|
||||||
to: to,
|
from: from,
|
||||||
body: body,
|
to: to,
|
||||||
subject: nil,
|
body: body,
|
||||||
thread: nil,
|
subject: nil,
|
||||||
oobUrl: nil,
|
thread: nil,
|
||||||
date: Date(),
|
oobUrl: nil,
|
||||||
pending: true,
|
date: Date(),
|
||||||
sentError: false
|
pending: true,
|
||||||
)
|
sentError: false
|
||||||
try database._db.write { db in
|
)
|
||||||
try message.insert(db)
|
try database._db.write { db in
|
||||||
|
try message.insert(db)
|
||||||
|
}
|
||||||
|
promise(.success(.xmppAction(.xmppMessageSent(message))))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
||||||
}
|
}
|
||||||
promise(.success(.xmppAction(.xmppMessageSent(message))))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,22 +233,24 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
case .xmppAction(.xmppMessageSendSuccess(let msgId)):
|
case .xmppAction(.xmppMessageSendSuccess(let msgId)):
|
||||||
// mark message as pending false and sentError false
|
// mark message as pending false and sentError false
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == msgId)
|
try Message
|
||||||
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: false))
|
.filter(Column("id") == msgId)
|
||||||
|
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as sent")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as sent")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,21 +258,23 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
case .xmppAction(.xmppMessageSendFailed(let msgId)):
|
case .xmppAction(.xmppMessageSendFailed(let msgId)):
|
||||||
// mark message as pending false and sentError true
|
// mark message as pending false and sentError true
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
guard let database = self?.database else {
|
||||||
return
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == msgId)
|
try Message
|
||||||
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
|
.filter(Column("id") == msgId)
|
||||||
|
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as failed to send")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(msgId) marked in db as failed to send")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,92 +282,100 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
// MARK: Attachments
|
// MARK: Attachments
|
||||||
case .fileAction(.downloadAttachmentFile(let id, _)):
|
case .fileAction(.downloadAttachmentFile(let id, _)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == id)
|
try Message
|
||||||
.updateAll(db, Column("attachmentDownloadFailed").set(to: false))
|
.filter(Column("id") == id)
|
||||||
|
.updateAll(db, Column("attachmentDownloadFailed").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as starting downloading attachment")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as starting downloading attachment")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.downloadingAttachmentFileFailed(let id, _)):
|
case .fileAction(.downloadingAttachmentFileFailed(let id, _)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == id)
|
try Message
|
||||||
.updateAll(db, Column("attachmentDownloadFailed").set(to: true))
|
.filter(Column("id") == id)
|
||||||
|
.updateAll(db, Column("attachmentDownloadFailed").set(to: true))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as failed to download attachment")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as failed to download attachment")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
|
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == id)
|
try Message
|
||||||
.updateAll(db, Column("attachmentLocalName").set(to: localName), Column("attachmentDownloadFailed").set(to: false))
|
.filter(Column("id") == id)
|
||||||
|
.updateAll(db, Column("attachmentLocalName").set(to: localName), Column("attachmentDownloadFailed").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as downloaded attachment")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as downloaded attachment")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.attachmentThumbnailCreated(let id, let thumbnailName)):
|
case .fileAction(.attachmentThumbnailCreated(let id, let thumbnailName)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == id)
|
try Message
|
||||||
.updateAll(db, Column("attachmentThumbnailName").set(to: thumbnailName))
|
.filter(Column("id") == id)
|
||||||
|
.updateAll(db, Column("attachmentThumbnailName").set(to: thumbnailName))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as thumbnail created")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(id) marked in db as thumbnail created")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: id, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,108 +383,116 @@ final class DatabaseMiddleware {
|
||||||
|
|
||||||
// MARK: Sharing
|
// MARK: Sharing
|
||||||
case .conversationAction(.sendMediaMessages(let from, let to, let messageIds, let localFilesNames)):
|
case .conversationAction(.sendMediaMessages(let from, let to, let messageIds, let localFilesNames)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
for (index, id) in messageIds.enumerated() {
|
do {
|
||||||
let message = Message(
|
for (index, id) in messageIds.enumerated() {
|
||||||
id: id,
|
let message = Message(
|
||||||
type: .chat,
|
id: id,
|
||||||
contentType: .attachment,
|
type: .chat,
|
||||||
from: from,
|
contentType: .attachment,
|
||||||
to: to,
|
from: from,
|
||||||
body: nil,
|
to: to,
|
||||||
subject: nil,
|
body: nil,
|
||||||
thread: nil,
|
subject: nil,
|
||||||
oobUrl: nil,
|
thread: nil,
|
||||||
date: Date(),
|
oobUrl: nil,
|
||||||
pending: true,
|
date: Date(),
|
||||||
sentError: false,
|
pending: true,
|
||||||
attachmentType: localFilesNames[index].attachmentType,
|
sentError: false,
|
||||||
attachmentLocalName: localFilesNames[index]
|
attachmentType: localFilesNames[index].attachmentType,
|
||||||
|
attachmentLocalName: localFilesNames[index]
|
||||||
|
)
|
||||||
|
try database._db.write { db in
|
||||||
|
try message.insert(db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: messages with sharings stored in db")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
||||||
)
|
)
|
||||||
try database._db.write { db in
|
|
||||||
try message.insert(db)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: messages with sharings stored in db")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .sharingAction(.retrySharing(let id)):
|
case .sharingAction(.retrySharing(let id)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.storeMessageFailed(reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == id)
|
try Message
|
||||||
.updateAll(db, Column("pending").set(to: true), Column("sentError").set(to: false))
|
.filter(Column("id") == id)
|
||||||
|
.updateAll(db, Column("pending").set(to: true), Column("sentError").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: message \(id) with shares marked in db as pending to send")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: message \(id) with shares marked in db as pending to send")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.storeMessageFailed(reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppSharingUploadSuccess(let messageId, let remotePath)):
|
case .xmppAction(.xmppSharingUploadSuccess(let messageId, let remotePath)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == messageId)
|
try Message
|
||||||
.updateAll(db, Column("attachmentRemotePath").set(to: remotePath), Column("pending").set(to: false), Column("sentError").set(to: false))
|
.filter(Column("id") == messageId)
|
||||||
|
.updateAll(db, Column("attachmentRemotePath").set(to: remotePath), Column("pending").set(to: false), Column("sentError").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: shared file uploaded and message \(messageId) marked in db as sent")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: shared file uploaded and message \(messageId) marked in db as sent")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppSharingUploadFailed(let messageId, _)):
|
case .xmppAction(.xmppSharingUploadFailed(let messageId, _)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
Task(priority: .background) { [weak self] in
|
Future<AppAction, Never> { promise in
|
||||||
guard let database = self?.database else {
|
Task(priority: .background) { [weak self] in
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
guard let database = self?.database else {
|
||||||
)
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: L10n.Global.Error.genericDbError)))
|
||||||
return
|
)
|
||||||
}
|
return
|
||||||
do {
|
}
|
||||||
_ = try database._db.write { db in
|
do {
|
||||||
try Message
|
_ = try database._db.write { db in
|
||||||
.filter(Column("id") == messageId)
|
try Message
|
||||||
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
|
.filter(Column("id") == messageId)
|
||||||
|
.updateAll(db, Column("pending").set(to: false), Column("sentError").set(to: true))
|
||||||
|
}
|
||||||
|
promise(.success(.info("DatabaseMiddleware: shared file upload failed and message \(messageId) marked in db as failed to send")))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
promise(.success(.info("DatabaseMiddleware: shared file upload failed and message \(messageId) marked in db as failed to send")))
|
|
||||||
} catch {
|
|
||||||
promise(.success(.databaseAction(.updateAttachmentFailed(id: messageId, reason: error.localizedDescription)))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,108 +10,126 @@ final class FileMiddleware {
|
||||||
switch action {
|
switch action {
|
||||||
// MARK: - For incomig attachments
|
// MARK: - For incomig attachments
|
||||||
case .conversationAction(.messagesUpdated(let messages)):
|
case .conversationAction(.messagesUpdated(let messages)):
|
||||||
return Future { [weak self] promise in
|
return Deferred {
|
||||||
guard let wSelf = self else {
|
Future { [weak self] promise in
|
||||||
promise(.success(.info("FileMiddleware: on checking attachments/shares messages, middleware self is nil")))
|
guard let wSelf = self else {
|
||||||
return
|
promise(.success(.info("FileMiddleware: on checking attachments/shares messages, middleware self is nil")))
|
||||||
}
|
return
|
||||||
|
|
||||||
// for incoming messages with attachments
|
|
||||||
for message in messages where message.attachmentRemotePath != nil && message.attachmentLocalPath == nil {
|
|
||||||
if wSelf.downloadingMessageIDs.contains(message.id) {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
wSelf.downloadingMessageIDs.insert(message.id)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
// swiftlint:disable:next force_unwrapping
|
|
||||||
store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: message.attachmentRemotePath!)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for outgoing messages with shared attachments
|
// for incoming messages with attachments
|
||||||
for message in messages where message.attachmentLocalPath != nil && message.attachmentRemotePath == nil && message.pending {
|
for message in messages where message.attachmentRemotePath != nil && message.attachmentLocalPath == nil {
|
||||||
DispatchQueue.main.async {
|
if wSelf.downloadingMessageIDs.contains(message.id) {
|
||||||
store.dispatch(.xmppAction(.xmppSharingTryUpload(message)))
|
continue
|
||||||
}
|
}
|
||||||
}
|
wSelf.downloadingMessageIDs.insert(message.id)
|
||||||
|
|
||||||
// for outgoing messages with shared attachments which are already uploaded
|
|
||||||
// but have no thumbnail (only for images)
|
|
||||||
for message in messages where !message.pending && !message.sentError && message.attachmentType == .image {
|
|
||||||
if message.attachmentLocalName != nil && message.attachmentRemotePath != nil && message.attachmentThumbnailName == nil {
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// swiftlint:disable:next force_unwrapping
|
// swiftlint:disable:next force_unwrapping
|
||||||
store.dispatch(.fileAction(.createAttachmentThumbnail(messageId: message.id, localName: message.attachmentLocalName!)))
|
store.dispatch(.fileAction(.downloadAttachmentFile(messageId: message.id, attachmentRemotePath: message.attachmentRemotePath!)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
promise(.success(.info("FileMiddleware: attachments/shares messages processed")))
|
// for outgoing messages with shared attachments
|
||||||
}.eraseToAnyPublisher()
|
for message in messages where message.attachmentLocalPath != nil && message.attachmentRemotePath == nil && message.pending {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
store.dispatch(.xmppAction(.xmppSharingTryUpload(message)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for outgoing messages with shared attachments which are already uploaded
|
||||||
|
// but have no thumbnail (only for images)
|
||||||
|
for message in messages where !message.pending && !message.sentError && message.attachmentType == .image {
|
||||||
|
if message.attachmentLocalName != nil && message.attachmentRemotePath != nil && message.attachmentThumbnailName == nil {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
// swiftlint:disable:next force_unwrapping
|
||||||
|
store.dispatch(.fileAction(.createAttachmentThumbnail(messageId: message.id, localName: message.attachmentLocalName!)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
promise(.success(.info("FileMiddleware: attachments/shares messages processed")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.downloadAttachmentFile(let id, let attachmentRemotePath)):
|
case .fileAction(.downloadAttachmentFile(let id, let attachmentRemotePath)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
let localName = "\(id)_\(UUID().uuidString)\(attachmentRemotePath.lastPathComponent)"
|
Future { promise in
|
||||||
let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
let localName = "\(id)_\(UUID().uuidString)\(attachmentRemotePath.lastPathComponent)"
|
||||||
DownloadManager.shared.enqueueDownload(from: attachmentRemotePath, to: localUrl) { error in
|
let localUrl = FileProcessing.fileFolder.appendingPathComponent(localName)
|
||||||
DispatchQueue.main.async {
|
DownloadManager.shared.enqueueDownload(from: attachmentRemotePath, to: localUrl) { error in
|
||||||
if let error {
|
DispatchQueue.main.async {
|
||||||
store.dispatch(.fileAction(.downloadingAttachmentFileFailed(messageId: id, reason: error.localizedDescription)))
|
if let error {
|
||||||
} else {
|
store.dispatch(.fileAction(.downloadingAttachmentFileFailed(messageId: id, reason: error.localizedDescription)))
|
||||||
store.dispatch(.fileAction(.attachmentFileDownloaded(messageId: id, localName: localName)))
|
} else {
|
||||||
|
store.dispatch(.fileAction(.attachmentFileDownloaded(messageId: id, localName: localName)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
promise(.success(.info("FileMiddleware: started downloading attachment for message \(id)")))
|
||||||
}
|
}
|
||||||
promise(.success(.info("FileMiddleware: started downloading attachment for message \(id)")))
|
}
|
||||||
}.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
|
case .fileAction(.attachmentFileDownloaded(let id, let localName)):
|
||||||
return Future { [weak self] promise in
|
return Deferred {
|
||||||
self?.downloadingMessageIDs.remove(id)
|
Future { [weak self] promise in
|
||||||
promise(.success(.fileAction(.createAttachmentThumbnail(messageId: id, localName: localName))))
|
self?.downloadingMessageIDs.remove(id)
|
||||||
|
promise(.success(.fileAction(.createAttachmentThumbnail(messageId: id, localName: localName))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.createAttachmentThumbnail(let id, let localName)):
|
case .fileAction(.createAttachmentThumbnail(let id, let localName)):
|
||||||
return Future { [weak self] promise in
|
return Deferred {
|
||||||
if let thumbnailName = FileProcessing.shared.createThumbnail(localName: localName) {
|
Future { [weak self] promise in
|
||||||
self?.downloadingMessageIDs.remove(id)
|
if let thumbnailName = FileProcessing.shared.createThumbnail(localName: localName) {
|
||||||
promise(.success(.fileAction(.attachmentThumbnailCreated(messageId: id, thumbnailName: thumbnailName))))
|
self?.downloadingMessageIDs.remove(id)
|
||||||
} else {
|
promise(.success(.fileAction(.attachmentThumbnailCreated(messageId: id, thumbnailName: thumbnailName))))
|
||||||
self?.downloadingMessageIDs.remove(id)
|
} else {
|
||||||
promise(.success(.info("FileMiddleware: failed to create thumbnail from \(localName) for message \(id)")))
|
self?.downloadingMessageIDs.remove(id)
|
||||||
|
promise(.success(.info("FileMiddleware: failed to create thumbnail from \(localName) for message \(id)")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
// MARK: - For outgoing sharing
|
// MARK: - For outgoing sharing
|
||||||
case .fileAction(.fetchItemsFromGallery):
|
case .fileAction(.fetchItemsFromGallery):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
let items = FileProcessing.shared.fetchGallery()
|
Future<AppAction, Never> { promise in
|
||||||
promise(.success(.fileAction(.itemsFromGalleryFetched(items: items))))
|
let items = FileProcessing.shared.fetchGallery()
|
||||||
|
promise(.success(.fileAction(.itemsFromGalleryFetched(items: items))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.itemsFromGalleryFetched(let items)):
|
case .fileAction(.itemsFromGalleryFetched(let items)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
let newItems = FileProcessing.shared.fillGalleryItemsThumbnails(items: items)
|
Future { promise in
|
||||||
promise(.success(.sharingAction(.galleryItemsUpdated(items: newItems))))
|
let newItems = FileProcessing.shared.fillGalleryItemsThumbnails(items: items)
|
||||||
|
promise(.success(.sharingAction(.galleryItemsUpdated(items: newItems))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.copyGalleryItemsForUploading(let items)):
|
case .fileAction(.copyGalleryItemsForUploading(let items)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
let ids = FileProcessing.shared.copyGalleryItemsForUploading(items: items)
|
Future { promise in
|
||||||
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 }))))
|
let ids = FileProcessing.shared.copyGalleryItemsForUploading(items: items)
|
||||||
|
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 }))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .fileAction(.copyCameraCapturedForUploading(let media, let type)):
|
case .fileAction(.copyCameraCapturedForUploading(let media, let type)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) {
|
Future { promise in
|
||||||
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName]))))
|
if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) {
|
||||||
} else {
|
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName]))))
|
||||||
promise(.success(.info("FileMiddleware: failed to copy camera captured media for uploading")))
|
} else {
|
||||||
|
promise(.success(.info("FileMiddleware: failed to copy camera captured media for uploading")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
|
@ -55,3 +55,15 @@ private var dateFormatter: DateFormatter {
|
||||||
formatter.dateFormat = "MM-dd HH:mm:ss.SSS"
|
formatter.dateFormat = "MM-dd HH:mm:ss.SSS"
|
||||||
return formatter
|
return formatter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For thread debugging
|
||||||
|
func ptInfo(_ message: String) {
|
||||||
|
#if DEBUG
|
||||||
|
let timeStr = dateFormatter.string(from: Date())
|
||||||
|
let str = "\(timeStr) \(message) -> \(Thread.current), \(String(validatingUTF8: __dispatch_queue_get_label(nil)) ?? "no queue label")"
|
||||||
|
print(str)
|
||||||
|
if isConsoleLoggingEnabled {
|
||||||
|
NSLog(str)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -7,18 +7,20 @@ final class MessagesMiddleware {
|
||||||
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
switch action {
|
switch action {
|
||||||
case .conversationAction(.makeConversationActive(let chat, let roster)):
|
case .conversationAction(.makeConversationActive(let chat, let roster)):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
if let currentClient = state.accountsState.accounts.first(where: { $0.bareJid == chat.account }) {
|
Future<AppAction, Never> { promise in
|
||||||
let features = state.accountsState.discoFeatures[currentClient.bareJid] ?? []
|
if let currentClient = state.accountsState.accounts.first(where: { $0.bareJid == chat.account }) {
|
||||||
if features.map({ $0.xep }).contains("XEP-0313") {
|
let features = state.accountsState.discoFeatures[currentClient.bareJid] ?? []
|
||||||
let oldestMessageDate = state.conversationsState.currentMessages.first?.date ?? Date()
|
if features.map({ $0.xep }).contains("XEP-0313") {
|
||||||
let archivesRequestDate = Calendar.current.date(byAdding: .day, value: -Const.mamRequestLength, to: oldestMessageDate) ?? Date()
|
let oldestMessageDate = state.conversationsState.currentMessages.first?.date ?? Date()
|
||||||
promise(.success(.xmppAction(.xmppLoadArchivedMessages(jid: currentClient.bareJid, to: roster?.bareJid, fromDate: archivesRequestDate))))
|
let archivesRequestDate = Calendar.current.date(byAdding: .day, value: -Const.mamRequestLength, to: oldestMessageDate) ?? Date()
|
||||||
|
promise(.success(.xmppAction(.xmppLoadArchivedMessages(jid: currentClient.bareJid, to: roster?.bareJid, fromDate: archivesRequestDate))))
|
||||||
|
} else {
|
||||||
|
promise(.success(.info("MessageMiddleware: XEP-0313 not supported for client \(currentClient.bareJid)")))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
promise(.success(.info("MessageMiddleware: XEP-0313 not supported for client \(currentClient.bareJid)")))
|
promise(.success(.info("MessageMiddleware: No client found for account \(chat.account), probably some error here")))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
promise(.success(.info("MessageMiddleware: No client found for account \(chat.account), probably some error here")))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
|
@ -11,43 +11,47 @@ final class SharingMiddleware {
|
||||||
switch action {
|
switch action {
|
||||||
// MARK: - Camera and Gallery Access
|
// MARK: - Camera and Gallery Access
|
||||||
case .sharingAction(.checkCameraAccess):
|
case .sharingAction(.checkCameraAccess):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
Future<AppAction, Never> { promise in
|
||||||
switch status {
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
||||||
case .authorized:
|
switch status {
|
||||||
promise(.success(.sharingAction(.setCameraAccess(true))))
|
case .authorized:
|
||||||
|
promise(.success(.sharingAction(.setCameraAccess(true))))
|
||||||
|
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
AVCaptureDevice.requestAccess(for: .video) { granted in
|
AVCaptureDevice.requestAccess(for: .video) { granted in
|
||||||
promise(.success(.sharingAction(.setCameraAccess(granted))))
|
promise(.success(.sharingAction(.setCameraAccess(granted))))
|
||||||
|
}
|
||||||
|
|
||||||
|
case .denied, .restricted:
|
||||||
|
promise(.success(.sharingAction(.setCameraAccess(false))))
|
||||||
|
|
||||||
|
@unknown default:
|
||||||
|
promise(.success(.sharingAction(.setCameraAccess(false))))
|
||||||
}
|
}
|
||||||
|
|
||||||
case .denied, .restricted:
|
|
||||||
promise(.success(.sharingAction(.setCameraAccess(false))))
|
|
||||||
|
|
||||||
@unknown default:
|
|
||||||
promise(.success(.sharingAction(.setCameraAccess(false))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .sharingAction(.checkGalleryAccess):
|
case .sharingAction(.checkGalleryAccess):
|
||||||
return Future<AppAction, Never> { promise in
|
return Deferred {
|
||||||
let status = PHPhotoLibrary.authorizationStatus()
|
Future<AppAction, Never> { promise in
|
||||||
switch status {
|
let status = PHPhotoLibrary.authorizationStatus()
|
||||||
case .authorized, .limited:
|
switch status {
|
||||||
promise(.success(.sharingAction(.setGalleryAccess(true))))
|
case .authorized, .limited:
|
||||||
|
promise(.success(.sharingAction(.setGalleryAccess(true))))
|
||||||
|
|
||||||
case .notDetermined:
|
case .notDetermined:
|
||||||
PHPhotoLibrary.requestAuthorization { status in
|
PHPhotoLibrary.requestAuthorization { status in
|
||||||
promise(.success(.sharingAction(.setGalleryAccess(status == .authorized))))
|
promise(.success(.sharingAction(.setGalleryAccess(status == .authorized))))
|
||||||
|
}
|
||||||
|
|
||||||
|
case .denied, .restricted:
|
||||||
|
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
||||||
|
|
||||||
|
@unknown default:
|
||||||
|
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
||||||
}
|
}
|
||||||
|
|
||||||
case .denied, .restricted:
|
|
||||||
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
|
||||||
|
|
||||||
@unknown default:
|
|
||||||
promise(.success(.sharingAction(.setGalleryAccess(false))))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -58,9 +62,11 @@ final class SharingMiddleware {
|
||||||
|
|
||||||
// MARK: - Sharing
|
// MARK: - Sharing
|
||||||
case .sharingAction(.shareMedia(let ids)):
|
case .sharingAction(.shareMedia(let ids)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
let items = state.sharingState.galleryItems.filter { ids.contains($0.id) }
|
Future { promise in
|
||||||
promise(.success(.fileAction(.copyGalleryItemsForUploading(items: items))))
|
let items = state.sharingState.galleryItems.filter { ids.contains($0.id) }
|
||||||
|
promise(.success(.fileAction(.copyGalleryItemsForUploading(items: items))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
@ -78,12 +84,14 @@ final class SharingMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case .sharingAction(.cameraCaptured(let media, let type)):
|
case .sharingAction(.cameraCaptured(let media, let type)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) {
|
Future { promise in
|
||||||
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName])))
|
if let (id, localName) = FileProcessing.shared.copyCameraCapturedForUploading(media: media, type: type) {
|
||||||
)
|
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: [id], localNames: [localName])))
|
||||||
} else {
|
)
|
||||||
promise(.success(.info("SharingMiddleware: camera's captured file didn't copied")))
|
} else {
|
||||||
|
promise(.success(.info("SharingMiddleware: camera's captured file didn't copied")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -98,10 +106,12 @@ final class SharingMiddleware {
|
||||||
}
|
}
|
||||||
|
|
||||||
case .sharingAction(.shareDocuments(let data, let extensions)):
|
case .sharingAction(.shareDocuments(let data, let extensions)):
|
||||||
return Future { promise in
|
return Deferred {
|
||||||
let ids = FileProcessing.shared.copyDocumentsForUploading(data: data, extensions: extensions)
|
Future { promise in
|
||||||
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 })))
|
let ids = FileProcessing.shared.copyDocumentsForUploading(data: data, extensions: extensions)
|
||||||
)
|
promise(.success(.fileAction(.itemsCopiedForUploading(newMessageIds: ids.map { $0.0 }, localNames: ids.map { $0.1 })))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
|
|
@ -39,70 +39,82 @@ final class XMPPMiddleware {
|
||||||
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
switch action {
|
switch action {
|
||||||
case .accountsAction(.tryAddAccountWithCredentials):
|
case .accountsAction(.tryAddAccountWithCredentials):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
self?.service.updateClients(for: state.accountsState.accounts)
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
self?.service.updateClients(for: state.accountsState.accounts)
|
||||||
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .accountsAction(.addAccountError):
|
case .accountsAction(.addAccountError):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
self?.service.updateClients(for: state.accountsState.accounts)
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
self?.service.updateClients(for: state.accountsState.accounts)
|
||||||
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .databaseAction(.storedAccountsLoaded(let accounts)):
|
case .databaseAction(.storedAccountsLoaded(let accounts)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp })
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp })
|
||||||
|
promise(.success(.info("XMPPMiddleware: clients updated in XMPP service")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .rostersAction(.addRoster(let ownerJID, let contactJID, let name, let groups)):
|
case .rostersAction(.addRoster(let ownerJID, let contactJID, let name, let groups)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
return promise(.success(.rostersAction(.addRosterError(reason: XMPPError.item_not_found.localizedDescription))))
|
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
||||||
}
|
return promise(.success(.rostersAction(.addRosterError(reason: XMPPError.item_not_found.localizedDescription))))
|
||||||
let module = client.modulesManager.module(RosterModule.self)
|
|
||||||
module.addItem(jid: JID(contactJID), name: name, groups: groups, completionHandler: { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
promise(.success(.rostersAction(.addRosterDone(jid: contactJID))))
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
promise(.success(.rostersAction(.addRosterError(reason: error.localizedDescription))))
|
|
||||||
}
|
}
|
||||||
})
|
let module = client.modulesManager.module(RosterModule.self)
|
||||||
|
module.addItem(jid: JID(contactJID), name: name, groups: groups, completionHandler: { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
promise(.success(.rostersAction(.addRosterDone(jid: contactJID))))
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
promise(.success(.rostersAction(.addRosterError(reason: error.localizedDescription))))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .rostersAction(.deleteRoster(let ownerJID, let contactJID)):
|
case .rostersAction(.deleteRoster(let ownerJID, let contactJID)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
return promise(.success(.rostersAction(.rosterDeletingFailed(reason: XMPPError.item_not_found.localizedDescription))))
|
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
||||||
}
|
return promise(.success(.rostersAction(.rosterDeletingFailed(reason: XMPPError.item_not_found.localizedDescription))))
|
||||||
let module = client.modulesManager.module(RosterModule.self)
|
|
||||||
module.removeItem(jid: JID(contactJID), completionHandler: { result in
|
|
||||||
switch result {
|
|
||||||
case .success:
|
|
||||||
promise(.success(.info("XMPPMiddleware: roster \(contactJID) deleted from \(ownerJID)")))
|
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription))))
|
|
||||||
}
|
}
|
||||||
})
|
let module = client.modulesManager.module(RosterModule.self)
|
||||||
|
module.removeItem(jid: JID(contactJID), completionHandler: { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
promise(.success(.info("XMPPMiddleware: roster \(contactJID) deleted from \(ownerJID)")))
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription))))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppMessageSent(let message)):
|
case .xmppAction(.xmppMessageSent(let message)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
DispatchQueue.global().async {
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
self?.service.sendMessage(message: message) { done in
|
DispatchQueue.global().async {
|
||||||
if done {
|
self?.service.sendMessage(message: message) { done in
|
||||||
promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id))))
|
if done {
|
||||||
} else {
|
promise(.success(.xmppAction(.xmppMessageSendSuccess(msgId: message.id))))
|
||||||
promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id))))
|
} else {
|
||||||
|
promise(.success(.xmppAction(.xmppMessageSendFailed(msgId: message.id))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,18 +122,20 @@ final class XMPPMiddleware {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppSharingTryUpload(let message)):
|
case .xmppAction(.xmppSharingTryUpload(let message)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
if self?.uploadingMessageIDs.contains(message.id) ?? false {
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
return promise(.success(.info("XMPPMiddleware: attachment in message \(message.id) is already in uploading process")))
|
if self?.uploadingMessageIDs.contains(message.id) ?? false {
|
||||||
} else {
|
return promise(.success(.info("XMPPMiddleware: attachment in message \(message.id) is already in uploading process")))
|
||||||
self?.uploadingMessageIDs.insert(message.id)
|
} else {
|
||||||
DispatchQueue.global().async {
|
self?.uploadingMessageIDs.insert(message.id)
|
||||||
self?.service.uploadAttachment(message: message) { error, remotePath in
|
DispatchQueue.global().async {
|
||||||
self?.uploadingMessageIDs.remove(message.id)
|
self?.service.uploadAttachment(message: message) { error, remotePath in
|
||||||
if let error {
|
self?.uploadingMessageIDs.remove(message.id)
|
||||||
promise(.success(.xmppAction(.xmppSharingUploadFailed(msgId: message.id, reason: error.localizedDescription))))
|
if let error {
|
||||||
} else {
|
promise(.success(.xmppAction(.xmppSharingUploadFailed(msgId: message.id, reason: error.localizedDescription))))
|
||||||
promise(.success(.xmppAction(.xmppSharingUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
|
} else {
|
||||||
|
promise(.success(.xmppAction(.xmppSharingUploadSuccess(msgId: message.id, attachmentRemotePath: remotePath))))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,9 +144,11 @@ final class XMPPMiddleware {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
case .xmppAction(.xmppLoadArchivedMessages(let jid, let to, let fromDate)):
|
case .xmppAction(.xmppLoadArchivedMessages(let jid, let to, let fromDate)):
|
||||||
return Future<AppAction, Never> { [weak self] promise in
|
return Deferred {
|
||||||
self?.service.requestArchivedMessages(jid: jid, to: to, fromDate: fromDate)
|
Future<AppAction, Never> { [weak self] promise in
|
||||||
promise(.success(.info("XMPPMiddleware: archived messages requested for \(jid) from \(fromDate)")))
|
self?.service.requestArchivedMessages(jid: jid, to: to, fromDate: fromDate)
|
||||||
|
promise(.success(.info("XMPPMiddleware: archived messages requested for \(jid) from \(fromDate)")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue