migrations UI

This commit is contained in:
Evgeny Poberezkin
2023-03-28 11:26:45 +01:00
parent db7b81587f
commit 8d8f7b2524
2 changed files with 73 additions and 59 deletions
@@ -14,11 +14,13 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.*
import chat.simplex.app.R
import chat.simplex.app.model.AppPreferences
import chat.simplex.app.model.DBEncryptionConfig
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
import chat.simplex.app.views.usersettings.NotificationsMode
@@ -40,45 +42,45 @@ fun DatabaseErrorView(
var useKeychain by remember { mutableStateOf(appPreferences.storeDBPassphrase.get()) }
val context = LocalContext.current
val restoreDbFromBackup = remember { mutableStateOf(shouldShowRestoreDbButton(appPreferences, context)) }
val saveAndRunChatOnClick: () -> Unit = {
fun callRunChat(confirmMigrations: MigrationConfirmation? = null) {
runChat(dbKey.value, confirmMigrations, chatDbStatus, progressIndicator, appPreferences)
}
fun saveAndRunChatOnClick() {
DatabaseUtils.setDatabaseKey(dbKey.value)
storedDBKey = dbKey.value
appPreferences.storeDBPassphrase.set(true)
useKeychain = true
appPreferences.initialRandomDBPassphrase.set(false)
runChat(dbKey.value, chatDbStatus, progressIndicator, appPreferences)
callRunChat()
}
@Composable
fun DatabaseErrorDetails(@StringRes title: Int, content: @Composable ColumnScope.() -> Unit) {
Text(
generalGetString(title),
Modifier.padding(start = 16.dp, top = 16.dp, bottom = 24.dp),
style = MaterialTheme.typography.h1
)
SectionView(null, padding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF), content)
}
@Composable
fun fileNameText(dbFile: String) {
Text(String.format(generalGetString(R.string.file_with_path), dbFile))
}
@Composable
fun migrationsText(ms: List<String>) {
Text(String.format(generalGetString(R.string.database_migrations), ms.joinToString(", ")))
}
// val status = chatDbStatus.value
// val title = when (status) {
// is DBMigrationResult.OK -> ""
// is DBMigrationResult.ErrorNotADatabase -> if (useKeychain && !storedDBKey.isNullOrEmpty())
// generalGetString(R.string.wrong_passphrase)
// else
// generalGetString(R.string.encrypted_database)
// is DBMigrationResult.ErrorMigration -> when (status.migrationError) {
// is MigrationError.Upgrade -> generalGetString(R.string.database_upgrade)
// is MigrationError.Downgrade -> generalGetString(R.string.database_downgrade)
// is MigrationError.Error -> generalGetString(R.string.incompatible_database_version)
// }
// is DBMigrationResult.ErrorSQL -> generalGetString(R.string.database_error)
// is DBMigrationResult.ErrorKeychain -> generalGetString(R.string.keychain_error)
// this can only happen if incorrect parameter is passed
// is DBMigrationResult.InvalidConfirmation -> "Invalid migration confirmation"
// is DBMigrationResult.Unknown -> generalGetString(R.string.database_error)
// null -> "" // should never be here
// }
Column(
Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Center,
) {
// Text(
// title,
// Modifier.padding(start = 16.dp, top = 16.dp, bottom = 24.dp),
// style = MaterialTheme.typography.h1
// )
// SectionView(null, padding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF)) {
val buttonEnabled = validKey(dbKey.value) && !progressIndicator.value
when (val status = chatDbStatus.value) {
is DBMigrationResult.ErrorNotADatabase ->
@@ -88,40 +90,49 @@ fun DatabaseErrorView(
DatabaseKeyField(dbKey, buttonEnabled) {
saveAndRunChatOnClick()
}
SaveAndOpenButton(buttonEnabled, saveAndRunChatOnClick)
SaveAndOpenButton(buttonEnabled, ::saveAndRunChatOnClick)
SectionSpacer()
Text(String.format(generalGetString(R.string.file_with_path), status.dbFile))
fileNameText(status.dbFile)
}
} else {
DatabaseErrorDetails(R.string.encrypted_database) {
Text(generalGetString(R.string.database_passphrase_is_required))
DatabaseKeyField(dbKey, buttonEnabled) {
if (useKeychain) saveAndRunChatOnClick() else runChat(dbKey.value, chatDbStatus, progressIndicator, appPreferences)
}
if (useKeychain) {
SaveAndOpenButton(buttonEnabled, saveAndRunChatOnClick)
DatabaseKeyField(dbKey, buttonEnabled, ::saveAndRunChatOnClick)
SaveAndOpenButton(buttonEnabled, ::saveAndRunChatOnClick)
} else {
OpenChatButton(buttonEnabled) { runChat(dbKey.value, chatDbStatus, progressIndicator, appPreferences) }
DatabaseKeyField(dbKey, buttonEnabled, { callRunChat() })
OpenChatButton(buttonEnabled) { callRunChat() }
}
}
}
is DBMigrationResult.ErrorMigration -> when (status.migrationError) {
is DBMigrationResult.ErrorMigration -> when (val err = status.migrationError) {
is MigrationError.Upgrade ->
DatabaseErrorDetails(R.string.database_upgrade) {
TextButton({ callRunChat(MigrationConfirmation.YesUp) }, Modifier.align(Alignment.CenterHorizontally), enabled = !progressIndicator.value) {
Text(generalGetString(R.string.upgrade_and_open_chat))
}
fileNameText(status.dbFile)
migrationsText(err.upMigrations.map { it.upName })
}
is MigrationError.Downgrade ->
DatabaseErrorDetails(R.string.database_downgrade) {
Text(generalGetString(R.string.database_downgrade_warning), fontWeight = FontWeight.Bold)
TextButton({ callRunChat(MigrationConfirmation.YesUpDown) }, Modifier.align(Alignment.CenterHorizontally), enabled = !progressIndicator.value) {
Text(generalGetString(R.string.downgrade_and_open_chat))
}
fileNameText(status.dbFile)
migrationsText(err.downMigrations)
}
is MigrationError.Error ->
DatabaseErrorDetails(R.string.incompatible_database_version) {
fileNameText(status.dbFile)
Text(String.format(generalGetString(R.string.error_with_info), mtrErrorDescription(err.mtrError)))
}
}
is DBMigrationResult.ErrorSQL ->
DatabaseErrorDetails(R.string.database_error) {
Text(String.format(generalGetString(R.string.file_with_path), status.dbFile))
fileNameText(status.dbFile)
Text(String.format(generalGetString(R.string.error_with_info), status.migrationSQLError))
}
is DBMigrationResult.ErrorKeychain ->
@@ -153,7 +164,6 @@ fun DatabaseErrorView(
)
}
}
// }
}
if (progressIndicator.value) {
Box(
@@ -171,18 +181,9 @@ fun DatabaseErrorView(
}
}
@Composable
private fun DatabaseErrorDetails(@StringRes title: Int, content: @Composable ColumnScope.() -> Unit) {
Text(
generalGetString(title),
Modifier.padding(start = 16.dp, top = 16.dp, bottom = 24.dp),
style = MaterialTheme.typography.h1
)
SectionView(null, padding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF), content)
}
private fun runChat(
dbKey: String,
confirmMigrations: MigrationConfirmation? = null,
chatDbStatus: State<DBMigrationResult?>,
progressIndicator: MutableState<Boolean>,
prefs: AppPreferences
@@ -191,7 +192,7 @@ private fun runChat(
if (progressIndicator.value) return@launch
progressIndicator.value = true
try {
SimplexApp.context.initChatController(dbKey)
SimplexApp.context.initChatController(dbKey, confirmMigrations)
} catch (e: Exception) {
Log.d(TAG, "initializeChat ${e.stackTraceToString()}")
}
@@ -204,18 +205,17 @@ private fun runChat(
NotificationsMode.PERIODIC.name -> SimplexApp.context.schedulePeriodicWakeUp()
}
}
is DBMigrationResult.ErrorNotADatabase -> {
is DBMigrationResult.ErrorNotADatabase ->
AlertManager.shared.showAlertMsg(generalGetString(R.string.wrong_passphrase_title), generalGetString(R.string.enter_correct_passphrase))
}
is DBMigrationResult.ErrorSQL -> {
is DBMigrationResult.ErrorSQL ->
AlertManager.shared.showAlertMsg(generalGetString(R.string.database_error), status.migrationSQLError)
}
is DBMigrationResult.ErrorKeychain -> {
is DBMigrationResult.ErrorKeychain ->
AlertManager.shared.showAlertMsg(generalGetString(R.string.keychain_error))
}
is DBMigrationResult.Unknown -> {
is DBMigrationResult.Unknown ->
AlertManager.shared.showAlertMsg(generalGetString(R.string.unknown_error), status.json)
}
is DBMigrationResult.InvalidConfirmation ->
AlertManager.shared.showAlertMsg(generalGetString(R.string.invalid_migration_confirmation))
is DBMigrationResult.ErrorMigration -> {}
null -> {}
}
}
@@ -245,6 +245,14 @@ private fun restoreDb(restoreDbFromBackup: MutableState<Boolean>, prefs: AppPref
}
}
private fun mtrErrorDescription(err: MTRError): String =
when (err) {
is MTRError.NoDown ->
String.format(generalGetString(R.string.mtr_error_no_down_migration), err.dbMigrations.joinToString(", "))
is MTRError.Different ->
String.format(generalGetString(R.string.mtr_error_different), err.appMigration, err.dbMigration)
}
@Composable
private fun DatabaseKeyField(text: MutableState<String>, enabled: Boolean, onClick: (() -> Unit)? = null) {
PassphraseField(
@@ -829,6 +829,12 @@
<string name="incompatible_database_version">Incompatible database version</string>
<string name="confirm_database_upgrades">Confirm database upgrades</string>
<string name="invalid_migration_confirmation">"Invalid migration confirmation"</string>
<string name="upgrade_and_open_chat">Upgrade and open chat</string>
<string name="downgrade_and_open_chat">Downgrade and open chat</string>
<string name="mtr_error_no_down_migration">database version is newer than the app, but no down migration for: %s</string>
<string name="mtr_error_different">different migration in the app/database: %s / %s</string>
<string name="database_migrations">Migrations: %s</string>
<string name="database_downgrade_warning">"Warning: you may lose some data!"</string>
<!-- ChatModel.chatRunning interactions -->
<string name="chat_is_stopped_indication">Chat is stopped</string>