From c9b1d54f1308841ced44ae4e19c3baa5e60fac83 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sat, 30 Dec 2023 22:04:01 +0000 Subject: [PATCH 1/5] docs: update downloads --- docs/DOWNLOADS.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/DOWNLOADS.md b/docs/DOWNLOADS.md index 5362e4f2c..bd88f7a17 100644 --- a/docs/DOWNLOADS.md +++ b/docs/DOWNLOADS.md @@ -7,7 +7,7 @@ revision: 25.11.2023 | Updated 25.11.2023 | Languages: EN | # Download SimpleX apps -The latest stable version is v5.4.1. +The latest stable version is v5.4.2. You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases). @@ -21,24 +21,24 @@ You can get the latest beta releases from [GitHub](https://github.com/simplex-ch Using the same profile as on mobile device is not yet supported – you need to create a separate profile to use desktop apps. -**Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-ubuntu-22_04-x86_64.deb). +**Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-ubuntu-22_04-x86_64.deb). -**Mac**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-macos-x86_64.dmg) (Intel), [aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-macos-aarch64.dmg) (Apple Silicon). +**Mac**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-macos-x86_64.dmg) (Intel), [aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-macos-aarch64.dmg) (Apple Silicon). -**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-desktop-windows-x86_64.msi). +**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-desktop-windows-x86_64.msi). ## Mobile apps **iOS**: [App store](https://apps.apple.com/us/app/simplex-chat/id1605771084), [TestFlight](https://testflight.apple.com/join/DWuT2LQu). -**Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-armv7a.apk). +**Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-armv7a.apk). ## Terminal (console) app See [Using terminal app](/docs/CLI.md). -**Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-chat-ubuntu-22_04-x86-64). +**Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-chat-ubuntu-22_04-x86-64). -**Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-chat-macos-x86-64), aarch64 - [compile from source](./CLI.md#). +**Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-chat-macos-x86-64), aarch64 - [compile from source](./CLI.md#). -**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.1/simplex-chat-windows-x86-64). +**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.4.2/simplex-chat-windows-x86-64). From e6b57270030e84606b49b6fb1ca2b0f929f256a9 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 2 Jan 2024 21:21:39 +0700 Subject: [PATCH 2/5] android, desktop: run with stopped chat (#3624) * android, desktop: run with stopped chat * way to prevent starting a chat in case of not saved database key * rename * change position of a call * new way of doing the same * better * exit process --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> --- .../java/chat/simplex/app/SimplexService.kt | 9 +- .../simplex/common/platform/UI.android.kt | 14 +-- .../chat/simplex/common/model/SimpleXAPI.kt | 26 ++--- .../chat/simplex/common/platform/Core.kt | 100 ++++++++++++------ .../views/database/DatabaseErrorView.kt | 7 +- .../common/views/database/DatabaseView.kt | 7 +- .../common/views/helpers/DatabaseUtils.kt | 1 - .../common/views/localauth/LocalAuthView.kt | 2 +- .../commonMain/resources/MR/base/strings.xml | 2 + 9 files changed, 100 insertions(+), 68 deletions(-) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt index 2cb6c12da..d1e0d9721 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/SimplexService.kt @@ -13,7 +13,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import chat.simplex.common.platform.Log import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat import androidx.work.* @@ -21,12 +20,13 @@ import chat.simplex.common.AppLock import chat.simplex.common.helpers.requiresIgnoringBattery import chat.simplex.common.model.ChatController import chat.simplex.common.model.NotificationsMode -import chat.simplex.common.platform.androidAppContext +import chat.simplex.common.platform.* import chat.simplex.common.views.helpers.* import kotlinx.coroutines.* import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +import kotlin.system.exitProcess // based on: // https://robertohuertas.com/2019/06/29/android_foreground_services/ @@ -173,6 +173,11 @@ class SimplexService: Service() { // Just to make sure that after restart of the app the user will need to re-authenticate AppLock.clearAuthState() + if (appPreferences.chatStopped.get()) { + stopSelf() + exitProcess(0) + } + // If notification service isn't enabled or battery optimization isn't disabled, we shouldn't restart the service if (!SimplexApp.context.allowToStartServiceAfterAppExit()) { return diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt index 371c14013..d360c44b4 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/UI.android.kt @@ -97,12 +97,14 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler { mainActivity.get()?.recreate() } else { mainActivity.get()?.apply { - window - ?.decorView - ?.findViewById(android.R.id.content) - ?.removeViewAt(0) - setContent { - AppScreen() + runOnUiThread { + window + ?.decorView + ?.findViewById(android.R.id.content) + ?.removeViewAt(0) + setContent { + AppScreen() + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 07e091b48..fc0d09772 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -106,6 +106,7 @@ class AppPreferences { val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null) val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null) val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null) + val chatStopped = mkBoolPreference(SHARED_PREFS_CHAT_STOPPED, false) val developerTools = mkBoolPreference(SHARED_PREFS_DEVELOPER_TOOLS, false) val terminalAlwaysVisible = mkBoolPreference(SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE, false) val networkUseSocksProxy = mkBoolPreference(SHARED_PREFS_NETWORK_USE_SOCKS_PROXY, false) @@ -273,6 +274,7 @@ class AppPreferences { private const val SHARED_PREFS_APP_LANGUAGE = "AppLanguage" private const val SHARED_PREFS_ONBOARDING_STAGE = "OnboardingStage" private const val SHARED_PREFS_CHAT_LAST_START = "ChatLastStart" + private const val SHARED_PREFS_CHAT_STOPPED = "ChatStopped" private const val SHARED_PREFS_DEVELOPER_TOOLS = "DeveloperTools" private const val SHARED_PREFS_TERMINAL_ALWAYS_VISIBLE = "TerminalAlwaysVisible" private const val SHARED_PREFS_NETWORK_USE_SOCKS_PROXY = "NetworkUseSocksProxy" @@ -346,14 +348,8 @@ object ChatController { try { if (chatModel.chatRunning.value == true) return apiSetNetworkConfig(getNetCfg()) - apiSetTempFolder(coreTmpDir.absolutePath) - apiSetFilesFolder(appFilesDir.absolutePath) - if (appPlatform.isDesktop) { - apiSetRemoteHostsFolder(remoteHostsDir.absolutePath) - } - apiSetXFTPConfig(getXFTPCfg()) - apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get()) val justStarted = apiStartChat() + appPrefs.chatStopped.set(false) val users = listUsers(null) chatModel.users.clear() chatModel.users.addAll(users) @@ -365,6 +361,9 @@ object ChatController { chatModel.chatRunning.value = true startReceiver() setLocalDeviceName(appPrefs.deviceNameForRemoteAccess.get()!!) + if (appPreferences.onboardingStage.get() == OnboardingStage.OnboardingComplete && !chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.get()) { + chatModel.setDeliveryReceipts.value = true + } Log.d(TAG, "startChat: started") } else { updatingChatsMutex.withLock { @@ -383,13 +382,6 @@ object ChatController { Log.d(TAG, "user: null") try { if (chatModel.chatRunning.value == true) return - apiSetTempFolder(coreTmpDir.absolutePath) - apiSetFilesFolder(appFilesDir.absolutePath) - if (appPlatform.isDesktop) { - apiSetRemoteHostsFolder(remoteHostsDir.absolutePath) - } - apiSetXFTPConfig(getXFTPCfg()) - apiSetEncryptLocalFiles(appPrefs.privacyEncryptLocalFiles.get()) chatModel.users.clear() chatModel.currentUser.value = null chatModel.localUserCreated.value = false @@ -596,19 +588,19 @@ object ChatController { } } - private suspend fun apiSetTempFolder(tempFolder: String) { + suspend fun apiSetTempFolder(tempFolder: String) { val r = sendCmd(null, CC.SetTempFolder(tempFolder)) if (r is CR.CmdOk) return throw Error("failed to set temp folder: ${r.responseType} ${r.details}") } - private suspend fun apiSetFilesFolder(filesFolder: String) { + suspend fun apiSetFilesFolder(filesFolder: String) { val r = sendCmd(null, CC.SetFilesFolder(filesFolder)) if (r is CR.CmdOk) return throw Error("failed to set files folder: ${r.responseType} ${r.details}") } - private suspend fun apiSetRemoteHostsFolder(remoteHostsFolder: String) { + suspend fun apiSetRemoteHostsFolder(remoteHostsFolder: String) { val r = sendCmd(null, CC.SetRemoteHostsFolder(remoteHostsFolder)) if (r is CR.CmdOk) return throw Error("failed to set remote hosts folder: ${r.responseType} ${r.details}") diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt index 07e59a55e..00a824438 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt @@ -1,8 +1,13 @@ package chat.simplex.common.platform import chat.simplex.common.model.* +import chat.simplex.common.model.ChatModel.controller +import chat.simplex.common.model.ChatModel.currentUser import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.helpers.DatabaseUtils.ksDatabasePassword import chat.simplex.common.views.onboarding.OnboardingStage +import chat.simplex.res.MR +import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import java.nio.ByteBuffer @@ -39,13 +44,17 @@ val chatController: ChatController = ChatController fun initChatControllerAndRunMigrations(ignoreSelfDestruct: Boolean) { if (ignoreSelfDestruct || DatabaseUtils.ksSelfDestructPassword.get() == null) { withBGApi { - initChatController() + if (appPreferences.chatStopped.get() && appPreferences.storeDBPassphrase.get() && ksDatabasePassword.get() != null) { + initChatController(startChat = ::showStartChatAfterRestartAlert) + } else { + initChatController() + } runMigrations() } } } -suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: Boolean = true) { +suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred = { CompletableDeferred(true) }) { try { chatModel.ctrlInitInProgress.value = true val dbKey = useKey ?: DatabaseUtils.useDatabaseKey() @@ -62,45 +71,66 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat chatModel.chatDbStatus.value = res if (res != DBMigrationResult.OK) { Log.d(TAG, "Unable to migrate successfully: $res") - } else if (startChat) { - // If we migrated successfully means previous re-encryption process on database level finished successfully too - if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null) - val user = chatController.apiGetActiveUser(null) - if (user == null) { - chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true) - chatModel.currentUser.value = null - chatModel.users.clear() - if (appPlatform.isDesktop) { - /** - * Setting it here to null because otherwise the screen will flash in [MainScreen] after the first start - * because of default value of [OnboardingStage.OnboardingComplete] - * */ - chatModel.localUserCreated.value = null - if (chatController.listRemoteHosts()?.isEmpty() == true) { - chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) - } - chatController.startChatWithoutUser() - } else { + return + } + controller.apiSetTempFolder(coreTmpDir.absolutePath) + controller.apiSetFilesFolder(appFilesDir.absolutePath) + if (appPlatform.isDesktop) { + controller.apiSetRemoteHostsFolder(remoteHostsDir.absolutePath) + } + controller.apiSetXFTPConfig(controller.getXFTPCfg()) + controller.apiSetEncryptLocalFiles(controller.appPrefs.privacyEncryptLocalFiles.get()) + // If we migrated successfully means previous re-encryption process on database level finished successfully too + if (appPreferences.encryptionStartedAt.get() != null) appPreferences.encryptionStartedAt.set(null) + val user = chatController.apiGetActiveUser(null) + chatModel.currentUser.value = user + if (user == null) { + chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true) + chatModel.currentUser.value = null + chatModel.users.clear() + if (appPlatform.isDesktop) { + /** + * Setting it here to null because otherwise the screen will flash in [MainScreen] after the first start + * because of default value of [OnboardingStage.OnboardingComplete] + * */ + chatModel.localUserCreated.value = null + if (chatController.listRemoteHosts()?.isEmpty() == true) { chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) } + chatController.startChatWithoutUser() } else { - val savedOnboardingStage = appPreferences.onboardingStage.get() - val newStage = if (listOf(OnboardingStage.Step1_SimpleXInfo, OnboardingStage.Step2_CreateProfile).contains(savedOnboardingStage) && chatModel.users.size == 1) { - OnboardingStage.Step3_CreateSimpleXAddress - } else { - savedOnboardingStage - } - if (appPreferences.onboardingStage.get() != newStage) { - appPreferences.onboardingStage.set(newStage) - } - if (appPreferences.onboardingStage.get() == OnboardingStage.OnboardingComplete && !chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.get()) { - chatModel.setDeliveryReceipts.value = true - } - chatController.startChat(user) - platform.androidChatInitializedAndStarted() + chatController.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) } + } else if (startChat().await()) { + val savedOnboardingStage = appPreferences.onboardingStage.get() + val newStage = if (listOf(OnboardingStage.Step1_SimpleXInfo, OnboardingStage.Step2_CreateProfile).contains(savedOnboardingStage) && chatModel.users.size == 1) { + OnboardingStage.Step3_CreateSimpleXAddress + } else { + savedOnboardingStage + } + if (appPreferences.onboardingStage.get() != newStage) { + appPreferences.onboardingStage.set(newStage) + } + chatController.startChat(user) + platform.androidChatInitializedAndStarted() + } else { + chatController.getUserChatData(null) + chatModel.localUserCreated.value = currentUser.value != null + chatModel.chatRunning.value = false } } finally { chatModel.ctrlInitInProgress.value = false } } + +fun showStartChatAfterRestartAlert(): CompletableDeferred { + val deferred = CompletableDeferred() + AlertManager.shared.showAlertDialog( + title = generalGetString(MR.strings.start_chat_question), + text = generalGetString(MR.strings.chat_is_stopped_you_should_transfer_database), + onConfirm = { deferred.complete(true) }, + onDismiss = { deferred.complete(false) }, + onDismissRequest = { deferred.complete(false) } + ) + return deferred +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseErrorView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseErrorView.kt index 22d69de1c..0c208c06e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseErrorView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseErrorView.kt @@ -44,7 +44,7 @@ fun DatabaseErrorView( fun callRunChat(confirmMigrations: MigrationConfirmation? = null) { val useKey = if (useKeychain) null else dbKey.value - runChat(useKey, confirmMigrations, chatDbStatus, progressIndicator, appPreferences) + runChat(useKey, confirmMigrations, chatDbStatus, progressIndicator) } fun saveAndRunChatOnClick() { @@ -190,13 +190,14 @@ private fun runChat( confirmMigrations: MigrationConfirmation? = null, chatDbStatus: State, progressIndicator: MutableState, - prefs: AppPreferences ) = CoroutineScope(Dispatchers.Default).launch { // Don't do things concurrently. Shouldn't be here concurrently, just in case if (progressIndicator.value) return@launch progressIndicator.value = true try { - initChatController(dbKey, confirmMigrations) + initChatController(dbKey, confirmMigrations, + startChat = if (appPreferences.chatStopped.get()) ::showStartChatAfterRestartAlert else { { CompletableDeferred(true) } } + ) } catch (e: Exception) { Log.d(TAG, "initializeChat ${e.stackTraceToString()}") } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt index 5e1abb684..3769e0fc9 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt @@ -378,12 +378,12 @@ private fun startChat(m: ChatModel, chatLastStart: MutableState, chatD ModalManager.closeAllModalsEverywhere() return@withApi } - if (m.currentUser.value == null) { + val user = m.currentUser.value + if (user == null) { ModalManager.closeAllModalsEverywhere() return@withApi } else { - m.controller.apiStartChat() - m.chatRunning.value = true + m.controller.startChat(user) } val ts = Clock.System.now() m.controller.appPrefs.chatLastStart.set(ts) @@ -453,6 +453,7 @@ private fun stopChat(m: ChatModel) { suspend fun stopChatAsync(m: ChatModel) { m.controller.apiStopChat() m.chatRunning.value = false + controller.appPrefs.chatStopped.set(true) } suspend fun deleteChatAsync(m: ChatModel) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt index c984e1645..cc06716da 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DatabaseUtils.kt @@ -82,7 +82,6 @@ sealed class DBMigrationResult { @Serializable @SerialName("unknown") data class Unknown(val json: String): DBMigrationResult() } - enum class MigrationConfirmation(val value: String) { YesUp("yesUp"), YesUpDown ("yesUpDown"), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt index 040190652..b758ecdcf 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt @@ -84,7 +84,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: ( m.chatDbChanged.value = true m.chatDbStatus.value = null try { - initChatController(startChat = true) + initChatController() } catch (e: Exception) { Log.d(TAG, "initializeChat ${e.stackTraceToString()}") } diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index bd61d9473..1cf1922c5 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -1112,6 +1112,8 @@ Chat is stopped You can start chat via app Settings / Database or by restarting the app. + Start chat? + Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. Chat archive From f758a5526a1e650134629b4f66d7edfdb42ce4ee Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 2 Jan 2024 21:28:36 +0700 Subject: [PATCH 3/5] android, desktop: specifying text color globally (#3635) * android, desktop: specifying text color globally * typography --- .../chat/simplex/common/ui/theme/Theme.kt | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt index 6af3156ca..21bee2b2b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.* +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatController import chat.simplex.common.platform.isInNightMode @@ -282,8 +283,27 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) { val theme by CurrentColors.collectAsState() MaterialTheme( colors = theme.colors, - typography = Typography, + typography = Typography.copy( + h1 = Typography.h1.copy(color = theme.colors.onBackground), + h2 = Typography.h2.copy(color = theme.colors.onBackground), + h3 = Typography.h3.copy(color = theme.colors.onBackground), + h4 = Typography.h4.copy(color = theme.colors.onBackground), + h5 = Typography.h5.copy(color = theme.colors.onBackground), + h6 = Typography.h6.copy(color = theme.colors.onBackground), + subtitle1 = Typography.subtitle1.copy(color = theme.colors.onBackground), + subtitle2 = Typography.subtitle2.copy(color = theme.colors.onBackground), + body1 = Typography.body1.copy(color = theme.colors.onBackground), + body2 = Typography.body2.copy(color = theme.colors.onBackground), + button = Typography.button.copy(color = theme.colors.onBackground), + caption = Typography.caption.copy(color = theme.colors.onBackground), + overline = Typography.overline.copy(color = theme.colors.onBackground) + ), shapes = Shapes, - content = content + content = { + ProvideTextStyle( + value = TextStyle(color = theme.colors.onBackground), + content = content + ) + } ) } From d00977790129403199510a22c27c0bea1f966531 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 2 Jan 2024 21:38:28 +0700 Subject: [PATCH 4/5] android, desktop: close gallery when media was deleted (#3636) --- .../kotlin/chat/simplex/common/views/chat/ChatView.kt | 4 +++- .../simplex/common/views/chat/item/ImageFullScreenView.kt | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 69a1b50e2..d6e75feeb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -1353,6 +1353,8 @@ private fun providerForGallery( fun item(skipInternalIndex: Int, initialChatId: Long): Pair? { var processedInternalIndex = -skipInternalIndex.sign val indexOfFirst = chatItems.indexOfFirst { it.id == initialChatId } + // The first was deleted or moderated + if (indexOfFirst == -1) return null for (chatItemsIndex in if (skipInternalIndex >= 0) indexOfFirst downTo 0 else indexOfFirst..chatItems.lastIndex) { val item = chatItems[chatItemsIndex] if (canShowMedia(item)) { @@ -1402,7 +1404,7 @@ private fun providerForGallery( override fun scrollToStart() { initialIndex = 0 - initialChatId = chatItems.first { canShowMedia(it) }.id + initialChatId = chatItems.firstOrNull { canShowMedia(it) }?.id ?: return } override fun onDismiss(index: Int) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt index c7268592b..825211ac8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/ImageFullScreenView.kt @@ -86,6 +86,8 @@ fun ImageFullScreenView(imageProvider: () -> ImageGalleryProvider, close: () -> provider.scrollToStart() pagerState.scrollToPage(0) } + // Current media was deleted or moderated, close gallery + index -> close() } } } From 767522e70184fae1c388edc125ae27a2388770c7 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Wed, 3 Jan 2024 03:20:05 +0700 Subject: [PATCH 5/5] ios: better way of starting chat after stop (#3637) Co-authored-by: Avently --- apps/ios/Shared/Model/SimpleXAPI.swift | 34 +++++++++++++++++-- apps/ios/Shared/Model/SuspendChat.swift | 18 +++------- .../Views/Database/DatabaseErrorView.swift | 2 +- 3 files changed, 37 insertions(+), 17 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index ddac78c3d..2dbb43eec 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -1212,7 +1212,7 @@ private func currentUserId(_ funcName: String) throws -> Int64 { throw RuntimeError("\(funcName): no current user") } -func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool = true, confirmMigrations: MigrationConfirmation? = nil) throws { +func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = nil, refreshInvitations: Bool = true, confirmMigrations: MigrationConfirmation? = nil) throws { logger.debug("initializeChat") let m = ChatModel.shared (m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations) @@ -1231,7 +1231,37 @@ func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool onboardingStageDefault.set(.step1_SimpleXInfo) privacyDeliveryReceiptsSet.set(true) m.onboardingStage = .step1_SimpleXInfo - } else if start { + } else if confirmStart { + showStartChatAfterRestartAlert { start in + do { + if start { AppChatState.shared.set(.active) } + try chatInitialized(start: start, refreshInvitations: refreshInvitations) + } catch let error { + logger.error("ChatInitialized error: \(error)") + } + } + } else { + try chatInitialized(start: start, refreshInvitations: refreshInvitations) + } +} + +func showStartChatAfterRestartAlert(result: @escaping (_ start: Bool) -> Void) { + AlertManager.shared.showAlert(Alert( + title: Text("Start chat?"), + message: Text("Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat."), + primaryButton: .default(Text("Ok")) { + result(true) + }, + secondaryButton: .cancel { + result(false) + } + )) +} + +private func chatInitialized(start: Bool, refreshInvitations: Bool) throws { + let m = ChatModel.shared + if m.currentUser == nil { return } + if start { try startChat(refreshInvitations: refreshInvitations) } else { m.chatRunning = false diff --git a/apps/ios/Shared/Model/SuspendChat.swift b/apps/ios/Shared/Model/SuspendChat.swift index 9b03f38f3..965e567aa 100644 --- a/apps/ios/Shared/Model/SuspendChat.swift +++ b/apps/ios/Shared/Model/SuspendChat.swift @@ -105,26 +105,16 @@ func initChatAndMigrate(refreshInvitations: Bool = true) { let m = ChatModel.shared if (!m.chatInitialized) { m.v3DBMigration = v3DBMigrationDefault.get() - if AppChatState.shared.value == .stopped { - AlertManager.shared.showAlert(Alert( - title: Text("Start chat?"), - message: Text("Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat."), - primaryButton: .default(Text("Ok")) { - AppChatState.shared.set(.active) - initialize(start: true) - }, - secondaryButton: .cancel { - initialize(start: false) - } - )) + if AppChatState.shared.value == .stopped && storeDBPassphraseGroupDefault.get() && kcDatabasePassword.get() != nil { + initialize(start: true, confirmStart: true) } else { initialize(start: true) } } - func initialize(start: Bool) { + func initialize(start: Bool, confirmStart: Bool = false) { do { - try initializeChat(start: m.v3DBMigration.startChat && start, refreshInvitations: refreshInvitations) + try initializeChat(start: m.v3DBMigration.startChat && start, confirmStart: m.v3DBMigration.startChat && confirmStart, refreshInvitations: refreshInvitations) } catch let error { AlertManager.shared.showAlertMsg( title: start ? "Error starting chat" : "Error opening chat", diff --git a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift index 04e377f3a..52ded4478 100644 --- a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift @@ -149,7 +149,7 @@ struct DatabaseErrorView: View { private func runChatSync(confirmMigrations: MigrationConfirmation? = nil) { do { resetChatCtrl() - try initializeChat(start: m.v3DBMigration.startChat, dbKey: useKeychain ? nil : dbKey, confirmMigrations: confirmMigrations) + try initializeChat(start: m.v3DBMigration.startChat, confirmStart: m.v3DBMigration.startChat && AppChatState.shared.value == .stopped, dbKey: useKeychain ? nil : dbKey, confirmMigrations: confirmMigrations) if let s = m.chatDbStatus { status = s let am = AlertManager.shared