mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 09:44:15 +00:00
ios, core: better notifications processing to avoid contention for database (#3485)
* core: forward notifications about message processing (for iOS notifications) * simplexmq * the option to keep database key, to allow re-opening the database * export new init with keepKey and reopen DB api * stop remote ctrl when suspending chat * ios: close/re-open db on suspend/activate * allow activating chat without restoring (for NSE) * update NSE to suspend/activate (does not work) * simplexmq * suspend chat and close database when last notification in the process is processed * stop reading notifications on message markers * replace async stream with cancellable concurrent queue * better synchronization of app and NSE * remove outside of task * remove unused var * whitespace * more debug logging, handle cancelled read after dequeue * comments * more comments
This commit is contained in:
@@ -41,7 +41,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio
|
||||
var cKey = dbKey.cString(using: .utf8)!
|
||||
var cConfirm = confirm.rawValue.cString(using: .utf8)!
|
||||
// the last parameter of chat_migrate_init is used to return the pointer to chat controller
|
||||
let cjson = chat_migrate_init(&cPath, &cKey, &cConfirm, &chatController)!
|
||||
let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, &chatController)!
|
||||
let dbRes = dbMigrationResult(fromCString(cjson))
|
||||
let encrypted = dbKey != ""
|
||||
let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey)
|
||||
@@ -57,6 +57,13 @@ public func chatCloseStore() {
|
||||
}
|
||||
}
|
||||
|
||||
public func chatReopenStore() {
|
||||
let err = fromCString(chat_reopen_store(getChatCtrl()))
|
||||
if err != "" {
|
||||
logger.error("chatReopenStore error: \(err)")
|
||||
}
|
||||
}
|
||||
|
||||
public func resetChatCtrl() {
|
||||
chatController = nil
|
||||
migrationResult = nil
|
||||
|
||||
@@ -27,7 +27,7 @@ public enum ChatCommand {
|
||||
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
|
||||
case startChat(subscribe: Bool, expire: Bool, xftp: Bool)
|
||||
case apiStopChat
|
||||
case apiActivateChat
|
||||
case apiActivateChat(restoreChat: Bool)
|
||||
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||
case setTempFolder(tempFolder: String)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
@@ -156,7 +156,7 @@ public enum ChatCommand {
|
||||
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
|
||||
case let .startChat(subscribe, expire, xftp): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire)) xftp=\(onOff(xftp))"
|
||||
case .apiStopChat: return "/_stop"
|
||||
case .apiActivateChat: return "/_app activate"
|
||||
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
@@ -604,7 +604,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case callInvitations(callInvitations: [RcvCallInvitation])
|
||||
case ntfTokenStatus(status: NtfTknStatus)
|
||||
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
|
||||
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
|
||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||
// remote desktop responses/events
|
||||
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
||||
@@ -751,6 +752,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||
case .ntfToken: return "ntfToken"
|
||||
case .ntfMessages: return "ntfMessages"
|
||||
case .ntfMessage: return "ntfMessage"
|
||||
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
||||
case .remoteCtrlList: return "remoteCtrlList"
|
||||
case .remoteCtrlFound: return "remoteCtrlFound"
|
||||
@@ -898,6 +900,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .ntfTokenStatus(status): return String(describing: status)
|
||||
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
|
||||
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
|
||||
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))")
|
||||
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
||||
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)"
|
||||
|
||||
@@ -10,6 +10,7 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
let GROUP_DEFAULT_APP_STATE = "appState"
|
||||
let GROUP_DEFAULT_NSE_STATE = "nseState"
|
||||
let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
|
||||
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
|
||||
@@ -68,11 +69,21 @@ public func registerGroupDefaults() {
|
||||
|
||||
public enum AppState: String {
|
||||
case active
|
||||
case activating
|
||||
case bgRefresh
|
||||
case suspending
|
||||
case suspended
|
||||
case stopped
|
||||
|
||||
public var running: Bool {
|
||||
switch self {
|
||||
case .active: return true
|
||||
case .activating: return true
|
||||
case .bgRefresh: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public var inactive: Bool {
|
||||
switch self {
|
||||
case .suspending: return true
|
||||
@@ -84,12 +95,32 @@ public enum AppState: String {
|
||||
public var canSuspend: Bool {
|
||||
switch self {
|
||||
case .active: return true
|
||||
case .activating: return true
|
||||
case .bgRefresh: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum NSEState: String {
|
||||
case created
|
||||
case active
|
||||
case suspending
|
||||
case suspended
|
||||
|
||||
public var inactive: Bool {
|
||||
switch self {
|
||||
case .created: true
|
||||
case .suspended: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public var canSuspend: Bool {
|
||||
if case .active = self { true } else { false }
|
||||
}
|
||||
}
|
||||
|
||||
public enum DBContainer: String {
|
||||
case documents
|
||||
case group
|
||||
@@ -101,6 +132,16 @@ public let appStateGroupDefault = EnumDefault<AppState>(
|
||||
withDefault: .active
|
||||
)
|
||||
|
||||
public let nseStateGroupDefault = EnumDefault<NSEState>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_NSE_STATE,
|
||||
withDefault: .created
|
||||
)
|
||||
|
||||
public func allowBackgroundRefresh() -> Bool {
|
||||
appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive
|
||||
}
|
||||
|
||||
public let dbContainerGroupDefault = EnumDefault<DBContainer>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_DB_CONTAINER,
|
||||
|
||||
@@ -2016,7 +2016,8 @@ public enum ConnectionEntity: Decodable {
|
||||
}
|
||||
|
||||
public struct NtfMsgInfo: Decodable {
|
||||
|
||||
public var msgId: String
|
||||
public var msgTs: Date
|
||||
}
|
||||
|
||||
public struct AChatItem: Decodable {
|
||||
|
||||
@@ -16,10 +16,10 @@ extern void hs_init(int argc, char **argv[]);
|
||||
typedef void* chat_ctrl;
|
||||
|
||||
// the last parameter is used to return the pointer to chat controller
|
||||
extern char *chat_migrate_init(char *path, char *key, char *confirm, chat_ctrl *ctrl);
|
||||
extern char *chat_migrate_init_key(char *path, char *key, int keepKey, char *confirm, chat_ctrl *ctrl);
|
||||
extern char *chat_close_store(chat_ctrl ctl);
|
||||
extern char *chat_reopen_store(chat_ctrl ctl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctl, char *cmd);
|
||||
extern char *chat_recv_msg(chat_ctrl ctl);
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
|
||||
extern char *chat_parse_markdown(char *str);
|
||||
extern char *chat_parse_server(char *str);
|
||||
|
||||
Reference in New Issue
Block a user