android: Saving prefs alert on exit with unsaved changes (#1508)

* android: Saving prefs alert on exit with unsaved changes

* DIfferent implementation for AlertDialog with long buttons

* Braces

* Change

* Alignment

* Rename

* small changes

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
Stanislav Dmitrenko
2022-12-06 23:48:15 +03:00
committed by GitHub
parent 887b374bfc
commit ce11d58a76
11 changed files with 191 additions and 87 deletions

View File

@@ -63,8 +63,11 @@ fun ChatInfoView(
setContactAlias(chat.chatInfo.apiId, it, chatModel)
},
openPreferences = {
ModalManager.shared.showModal(true) {
ContactPreferencesView(chatModel, chatModel.currentUser.value ?: return@showModal, contact.contactId)
ModalManager.shared.showCustomModal { close ->
val user = chatModel.currentUser.value
if (user != null) {
ContactPreferencesView(chatModel, user, contact.contactId, close)
}
}
},
deleteContact = { deleteContactDialog(chat.chatInfo, chatModel, close) },

View File

@@ -25,33 +25,45 @@ fun ContactPreferencesView(
m: ChatModel,
user: User,
contactId: Long,
close: () -> Unit,
) {
val contact = remember { derivedStateOf { (m.getContactChat(contactId)?.chatInfo as? ChatInfo.Direct)?.contact } }
val ct = contact.value ?: return
var featuresAllowed by remember(ct) { mutableStateOf(contactUserPrefsToFeaturesAllowed(ct.mergedPreferences)) }
var currentFeaturesAllowed by remember(ct) { mutableStateOf(featuresAllowed) }
ContactPreferencesLayout(
featuresAllowed,
currentFeaturesAllowed,
user,
ct,
applyPrefs = { prefs ->
featuresAllowed = prefs
},
reset = {
featuresAllowed = currentFeaturesAllowed
},
savePrefs = {
withApi {
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
val toContact = m.controller.apiSetContactPrefs(ct.contactId, prefs)
if (toContact != null) {
m.updateContact(toContact)
currentFeaturesAllowed = featuresAllowed
}
fun savePrefs(afterSave: () -> Unit = {}) {
withApi {
val prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
val toContact = m.controller.apiSetContactPrefs(ct.contactId, prefs)
if (toContact != null) {
m.updateContact(toContact)
currentFeaturesAllowed = featuresAllowed
}
afterSave()
}
}
ModalView(
close = {
if (featuresAllowed == currentFeaturesAllowed) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
)
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
ContactPreferencesLayout(
featuresAllowed,
currentFeaturesAllowed,
user,
ct,
applyPrefs = { prefs ->
featuresAllowed = prefs
},
reset = {
featuresAllowed = currentFeaturesAllowed
},
savePrefs = ::savePrefs,
)
}
}
@Composable
@@ -139,3 +151,13 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
}
}
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(R.string.save_preferences_question),
confirmText = generalGetString(R.string.save_and_notify_contact),
dismissText = generalGetString(R.string.exit_without_saving),
onConfirm = save,
onDismiss = revert,
)
}

View File

@@ -44,8 +44,8 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
selectedRole = selectedRole,
allowModifyMembers = allowModifyMembers,
openPreferences = {
ModalManager.shared.showModal(true) {
GroupPreferencesView(chatModel, groupInfo.id)
ModalManager.shared.showCustomModal { close ->
GroupPreferencesView(chatModel, groupInfo.id, close)
}
},
inviteMembers = {

View File

@@ -65,10 +65,11 @@ fun GroupChatInfoView(chatModel: ChatModel, close: () -> Unit) {
ModalManager.shared.showCustomModal { close -> GroupProfileView(groupInfo, chatModel, close) }
},
openPreferences = {
ModalManager.shared.showModal(true) {
ModalManager.shared.showCustomModal { close ->
GroupPreferencesView(
chatModel,
chat.id
chat.id,
close
)
}
},

View File

@@ -13,41 +13,50 @@ import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import chat.simplex.app.R
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimplexGreen
import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
@Composable
fun GroupPreferencesView(m: ChatModel, chatId: String) {
fun GroupPreferencesView(m: ChatModel, chatId: String, close: () -> Unit,) {
val groupInfo = remember { derivedStateOf { (m.getChat(chatId)?.chatInfo as? ChatInfo.Group)?.groupInfo } }
val gInfo = groupInfo.value ?: return
var preferences by remember(gInfo) { mutableStateOf(gInfo.fullGroupPreferences) }
var currentPreferences by remember(gInfo) { mutableStateOf(preferences) }
GroupPreferencesLayout(
preferences,
currentPreferences,
gInfo,
applyPrefs = { prefs ->
preferences = prefs
},
reset = {
preferences = currentPreferences
},
savePrefs = {
withApi {
val gp = gInfo.groupProfile.copy(groupPreferences = preferences.toGroupPreferences())
val gInfo = m.controller.apiUpdateGroup(gInfo.groupId, gp)
if (gInfo != null) {
m.updateGroup(gInfo)
currentPreferences = preferences
}
fun savePrefs(afterSave: () -> Unit = {}) {
withApi {
val gp = gInfo.groupProfile.copy(groupPreferences = preferences.toGroupPreferences())
val gInfo = m.controller.apiUpdateGroup(gInfo.groupId, gp)
if (gInfo != null) {
m.updateGroup(gInfo)
currentPreferences = preferences
}
afterSave()
}
}
ModalView(
close = {
if (preferences == currentPreferences) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
)
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
GroupPreferencesLayout(
preferences,
currentPreferences,
gInfo,
applyPrefs = { prefs ->
preferences = prefs
},
reset = {
preferences = currentPreferences
},
savePrefs = ::savePrefs,
)
}
}
@Composable
@@ -130,3 +139,13 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
}
}
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(R.string.save_preferences_question),
confirmText = generalGetString(R.string.save_and_notify_group_members),
dismissText = generalGetString(R.string.exit_without_saving),
onConfirm = save,
onDismiss = revert,
)
}

View File

@@ -5,9 +5,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Dialog
import chat.simplex.app.R
import chat.simplex.app.TAG
@@ -93,6 +94,41 @@ class AlertManager {
}
}
fun showAlertDialogStacked(
title: String,
text: String? = null,
confirmText: String = generalGetString(R.string.ok),
onConfirm: (() -> Unit)? = null,
dismissText: String = generalGetString(R.string.cancel_verb),
onDismiss: (() -> Unit)? = null,
onDismissRequest: (() -> Unit)? = null,
destructive: Boolean = false
) {
val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) }
showAlert {
AlertDialog(
onDismissRequest = { onDismissRequest?.invoke(); hideAlert() },
title = { Text(title) },
text = alertText,
buttons = {
Column(
Modifier.fillMaxWidth().padding(horizontal = 8.dp).padding(top = 16.dp, bottom = 2.dp),
horizontalAlignment = Alignment.End
) {
TextButton(onClick = {
onDismiss?.invoke()
hideAlert()
}) { Text(dismissText) }
TextButton(onClick = {
onConfirm?.invoke()
hideAlert()
}) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) }
}
},
)
}
}
fun showAlertMsg(
title: String, text: String? = null,
confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null
@@ -128,4 +164,4 @@ class AlertManager {
companion object {
val shared = AlertManager()
}
}
}

View File

@@ -19,33 +19,40 @@ import chat.simplex.app.ui.theme.*
import chat.simplex.app.views.helpers.*
@Composable
fun PreferencesView(m: ChatModel, user: User) {
fun PreferencesView(m: ChatModel, user: User, close: () -> Unit,) {
var preferences by remember { mutableStateOf(user.fullPreferences) }
var currentPreferences by remember { mutableStateOf(preferences) }
PreferencesLayout(
preferences,
currentPreferences,
applyPrefs = { prefs ->
preferences = prefs
},
reset = {
preferences = currentPreferences
},
savePrefs = {
withApi {
val newProfile = user.profile.toProfile().copy(preferences = preferences.toPreferences())
val updatedProfile = m.controller.apiUpdateProfile(newProfile)
if (updatedProfile != null) {
val updatedUser = user.copy(
profile = updatedProfile.toLocalProfile(user.profile.profileId),
fullPreferences = preferences
)
currentPreferences = preferences
m.currentUser.value = updatedUser
}
fun savePrefs(afterSave: () -> Unit = {}) {
withApi {
val newProfile = user.profile.toProfile().copy(preferences = preferences.toPreferences())
val updatedProfile = m.controller.apiUpdateProfile(newProfile)
if (updatedProfile != null) {
val updatedUser = user.copy(
profile = updatedProfile.toLocalProfile(user.profile.profileId),
fullPreferences = preferences
)
currentPreferences = preferences
m.currentUser.value = updatedUser
}
afterSave()
}
}
ModalView(
close = {
if (preferences == currentPreferences) close()
else showUnsavedChangesAlert({ savePrefs(close) }, close)
},
)
background = if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight
) {
PreferencesLayout(
preferences,
currentPreferences,
applyPrefs = { preferences = it },
reset = { preferences = currentPreferences },
savePrefs = ::savePrefs,
)
}
}
@Composable
@@ -107,3 +114,13 @@ private fun ResetSaveButtons(reset: () -> Unit, save: () -> Unit, disabled: Bool
}
}
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(R.string.save_preferences_question),
confirmText = generalGetString(R.string.save_and_notify_contacts),
dismissText = generalGetString(R.string.exit_without_saving),
onConfirm = save,
onDismiss = revert,
)
}

View File

@@ -116,7 +116,7 @@ fun SettingsLayout(
SectionDivider()
SettingsActionItem(Icons.Outlined.QrCode, stringResource(R.string.your_simplex_contact_address), showModal { CreateLinkView(it, CreateLinkTab.LONG_TERM) }, disabled = stopped)
SectionDivider()
ChatPreferencesItem(showSettingsModal)
ChatPreferencesItem(showCustomModal)
}
SectionSpacer()
@@ -239,14 +239,14 @@ fun MaintainIncognitoState(chatModel: ChatModel) {
}
}
@Composable fun ChatPreferencesItem(showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)) {
@Composable fun ChatPreferencesItem(showCustomModal: ((@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit))) {
SettingsActionItem(
Icons.Outlined.ToggleOn,
stringResource(R.string.chat_preferences),
click = {
withApi {
showSettingsModal {
PreferencesView(it, it.currentUser.value ?: return@showSettingsModal)
showCustomModal { m, close ->
PreferencesView(m, m.currentUser.value ?: return@showCustomModal, close)
}()
}
}

View File

@@ -475,9 +475,11 @@
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.\n\n<xliff:g id="appName">SimpleX</xliff:g>-Server können Ihr Profil nicht sehen.</string>
<string name="edit_image">Bild bearbeiten</string>
<string name="delete_image">Bild löschen</string>
<string name="save_and_notify_contact">Speichern (und Kontakt benachrichtigen)</string>
<string name="save_and_notify_contacts">Speichern (und Kontakte benachrichtigen)</string>
<string name="save_and_notify_group_members">Speichern (und Gruppenmitglieder benachrichtigen)</string>
<string name="save_preferences_question">Präferenzen speichern?</string>
<string name="save_and_notify_contact">Speichern und Kontakt benachrichtigen</string>
<string name="save_and_notify_contacts">Speichern und Kontakte benachrichtigen</string>
<string name="save_and_notify_group_members">Speichern und Gruppenmitglieder benachrichtigen</string>
<string name="exit_without_saving">Beenden ohne Speichern</string>
<!-- Welcome Prompts - WelcomeView.kt -->
<string name="you_control_your_chat">Sie haben volle Kontrolle über Ihren Chat!</string>

View File

@@ -472,9 +472,11 @@
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.\n\n<xliff:g id="appName">SimpleX</xliff:g> серверы не могут получить доступ к вашему профилю.</string>
<string name="edit_image">Поменять аватар</string>
<string name="delete_image">Удалить аватар</string>
<string name="save_and_notify_contact">Сохранить (и уведомить контакт)</string>
<string name="save_and_notify_contacts">Сохранить (и послать обновление контактам)</string>
<string name="save_and_notify_group_members">Сохранить (и уведомить членов группы)</string>
<string name="save_preferences_question">Сохранить предпочтения?</string>
<string name="save_and_notify_contact">Сохранить и уведомить контакт</string>
<string name="save_and_notify_contacts">Сохранить и уведомить контакты</string>
<string name="save_and_notify_group_members">Сохранить и уведомить членов группы</string>
<string name="exit_without_saving">Выйти без сохранения</string>
<!-- Welcome Prompts - WelcomeView.kt -->
<string name="you_control_your_chat">Вы котролируете ваш чат!</string>

View File

@@ -475,9 +475,11 @@
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Your profile is stored on your device and shared only with your contacts.\n\n<xliff:g id="appName">SimpleX</xliff:g> servers cannot see your profile.</string>
<string name="edit_image">Edit image</string>
<string name="delete_image">Delete image</string>
<string name="save_and_notify_contact">Save (and notify contact)</string>
<string name="save_and_notify_contacts">Save (and notify contacts)</string>
<string name="save_and_notify_group_members">Save (and notify group members)</string>
<string name="save_preferences_question">Save preferences?</string>
<string name="save_and_notify_contact">Save and notify contact</string>
<string name="save_and_notify_contacts">Save and notify contacts</string>
<string name="save_and_notify_group_members">Save and notify group members</string>
<string name="exit_without_saving">Exit without saving</string>
<!-- Welcome Prompts - WelcomeView.kt -->
<string name="you_control_your_chat">You control your chat!</string>