From e66f5d488bcc52b2725311d186d695aec12eb033 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:11:00 +0300 Subject: [PATCH] android: more enhancements to layouts (#2196) * android: more enhancements to layouts * changes * unused code * unused code --- .../chat/simplex/app/views/WelcomeView.kt | 11 +- .../views/chat/group/AddGroupMembersView.kt | 12 + .../app/views/chat/group/GroupProfileView.kt | 88 ++++--- .../simplex/app/views/helpers/AlertManager.kt | 3 +- .../simplex/app/views/newchat/AddGroupView.kt | 17 +- .../app/views/onboarding/HowItWorks.kt | 5 + .../app/views/usersettings/UserProfileView.kt | 224 +++++++----------- 7 files changed, 169 insertions(+), 191 deletions(-) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt index 1d933ac762..cd066b9076 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/WelcomeView.kt @@ -32,6 +32,8 @@ import chat.simplex.app.views.onboarding.OnboardingStage import chat.simplex.app.views.onboarding.ReadableText import com.google.accompanist.insets.navigationBarsWithImePadding import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch fun isValidDisplayName(name: String) : Boolean { return (name.firstOrNull { it.isWhitespace() }) == null && !name.startsWith("@") && !name.startsWith("#") @@ -162,7 +164,7 @@ fun ProfileNameField(name: MutableState, placeholder: String = "", isVal .onFocusChanged { focused = it.isFocused } TextField( value = name.value, - onValueChange = { name.value = it; valid = isValid(it) }, + onValueChange = { name.value = it }, modifier = if (focusRequester == null) modifier else modifier.focusRequester(focusRequester), textStyle = TextStyle(fontSize = 18.sp, color = colors.onBackground), keyboardOptions = KeyboardOptions( @@ -182,4 +184,11 @@ fun ProfileNameField(name: MutableState, placeholder: String = "", isVal errorIndicatorColor = Color.Unspecified ) ) + LaunchedEffect(Unit) { + snapshotFlow { name.value } + .distinctUntilChanged() + .collect { + valid = isValid(it) + } + } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt index 1ff21e6651..07b9318ab6 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/AddGroupMembersView.kt @@ -28,6 +28,7 @@ import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.ChatInfoToolbarTitle import chat.simplex.app.views.helpers.* +import chat.simplex.app.views.newchat.InfoAboutIncognito import chat.simplex.app.views.usersettings.SettingsActionItem @Composable @@ -37,6 +38,7 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch var allowModifyMembers by remember { mutableStateOf(true) } BackHandler(onBack = close) AddGroupMembersLayout( + chatModel.incognito.value, groupInfo = groupInfo, creatingGroup = creatingGroup, contactsToAdd = getContactsToAdd(chatModel), @@ -85,6 +87,7 @@ fun getContactsToAdd(chatModel: ChatModel): List { @Composable fun AddGroupMembersLayout( + chatModelIncognito: Boolean, groupInfo: GroupInfo, creatingGroup: Boolean, contactsToAdd: List, @@ -105,6 +108,14 @@ fun AddGroupMembersLayout( horizontalAlignment = Alignment.Start, ) { AppBarTitle(stringResource(R.string.button_add_members)) + InfoAboutIncognito( + chatModelIncognito, + false, + generalGetString(R.string.group_unsupported_incognito_main_profile_sent), + generalGetString(R.string.group_main_profile_sent), + true + ) + Spacer(Modifier.size(DEFAULT_PADDING)) Row( Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center @@ -318,6 +329,7 @@ fun showProhibitedToInviteIncognitoAlertDialog() { fun PreviewAddGroupMembersLayout() { SimpleXTheme { AddGroupMembersLayout( + chatModelIncognito = false, groupInfo = GroupInfo.sampleData, creatingGroup = false, contactsToAdd = listOf(Contact.sampleData, Contact.sampleData, Contact.sampleData), diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt index 89271baf2e..3ad913d431 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/group/GroupProfileView.kt @@ -1,7 +1,6 @@ package chat.simplex.app.views.chat.group import android.content.res.Configuration -import android.graphics.Bitmap import android.net.Uri import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -24,6 +23,7 @@ import chat.simplex.app.ui.theme.* import chat.simplex.app.views.ProfileNameField import chat.simplex.app.views.helpers.* import chat.simplex.app.views.isValidDisplayName +import chat.simplex.app.views.onboarding.ReadableText import chat.simplex.app.views.usersettings.* import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding @@ -61,7 +61,25 @@ fun GroupProfileLayout( val scope = rememberCoroutineScope() val scrollState = rememberScrollState() val focusRequester = remember { FocusRequester() } - + val dataUnchanged = + displayName.value == groupProfile.displayName && + fullName.value == groupProfile.fullName && + chosenImage.value == null + val closeWithAlert = { + if (dataUnchanged || !(displayName.value.isNotEmpty() && isValidDisplayName(displayName.value))) { + close() + } else { + showUnsavedChangesAlert({ + saveProfile( + groupProfile.copy( + displayName = displayName.value, + fullName = fullName.value, + image = profileImage.value + ) + ) + }, close) + } + } ProvideWindowInsets(windowInsetsAnimationsEnabled = true) { ModalBottomSheetLayout( scrimColor = Color.Black.copy(alpha = 0.12F), @@ -77,23 +95,16 @@ fun GroupProfileLayout( sheetState = bottomSheetModalState, sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp) ) { - ModalView(close = close) { + ModalView(close = closeWithAlert) { Column( Modifier .verticalScroll(scrollState) - .padding(horizontal = DEFAULT_PADDING), - horizontalAlignment = Alignment.Start ) { - Text( - stringResource(R.string.group_profile_is_stored_on_members_devices), - Modifier.padding(bottom = 24.dp), - color = MaterialTheme.colors.onBackground, - lineHeight = 22.sp - ) Column( - Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.Start + Modifier.fillMaxWidth() + .padding(horizontal = DEFAULT_PADDING) ) { + ReadableText(R.string.group_profile_is_stored_on_members_devices, TextAlign.Center) Box( Modifier .fillMaxWidth() @@ -102,7 +113,7 @@ fun GroupProfileLayout( ) { Box(contentAlignment = Alignment.TopEnd) { Box(contentAlignment = Alignment.Center) { - ProfileImage(192.dp, profileImage.value) + ProfileImage(108.dp, profileImage.value, color = HighOrLowlight.copy(alpha = 0.1f)) EditImageButton { scope.launch { bottomSheetModalState.show() } } } if (profileImage.value != null) { @@ -133,32 +144,29 @@ fun GroupProfileLayout( ) ProfileNameField(fullName) Spacer(Modifier.height(DEFAULT_PADDING)) - Row { - TextButton(stringResource(R.string.cancel_verb)) { - close.invoke() - } - Spacer(Modifier.padding(horizontal = 8.dp)) - val enabled = displayName.value.isNotEmpty() && isValidDisplayName(displayName.value) - if (enabled) { - Text( - stringResource(R.string.save_group_profile), - modifier = Modifier.clickable { - saveProfile(groupProfile.copy( + val enabled = !dataUnchanged && displayName.value.isNotEmpty() && isValidDisplayName(displayName.value) + if (enabled) { + Text( + stringResource(R.string.save_group_profile), + modifier = Modifier.clickable { + saveProfile( + groupProfile.copy( displayName = displayName.value, fullName = fullName.value, image = profileImage.value - )) - }, - color = MaterialTheme.colors.primary - ) - } else { - Text( - stringResource(R.string.save_group_profile), - color = HighOrLowlight - ) - } + ) + ) + }, + color = MaterialTheme.colors.primary + ) + } else { + Text( + stringResource(R.string.save_group_profile), + color = HighOrLowlight + ) } } + Spacer(Modifier.height(DEFAULT_BOTTOM_BUTTON_PADDING)) LaunchedEffect(Unit) { @@ -171,6 +179,16 @@ fun GroupProfileLayout( } } +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, + ) +} + @Preview(showBackground = true) @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt index 03cabe4c83..97c4bfa51a 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/AlertManager.kt @@ -10,6 +10,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.* import androidx.compose.ui.window.Dialog import chat.simplex.app.R @@ -134,7 +135,7 @@ class AlertManager { TextButton(onClick = { onConfirm?.invoke() hideAlert() - }) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified) } + }) { Text(confirmText, color = if (destructive) MaterialTheme.colors.error else Color.Unspecified, textAlign = TextAlign.End) } } }, ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt index a4b64107bb..2cf1a1d90e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/newchat/AddGroupView.kt @@ -1,6 +1,5 @@ package chat.simplex.app.views.newchat -import android.graphics.Bitmap import android.net.Uri import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -28,6 +27,7 @@ import chat.simplex.app.views.chat.group.AddGroupMembersView import chat.simplex.app.views.chatlist.setGroupMembers import chat.simplex.app.views.helpers.* import chat.simplex.app.views.isValidDisplayName +import chat.simplex.app.views.onboarding.ReadableText import chat.simplex.app.views.usersettings.DeleteImageButton import chat.simplex.app.views.usersettings.EditImageButton import com.google.accompanist.insets.ProvideWindowInsets @@ -38,7 +38,6 @@ import kotlinx.coroutines.launch @Composable fun AddGroupView(chatModel: ChatModel, close: () -> Unit) { AddGroupLayout( - chatModel.incognito.value, createGroup = { groupProfile -> withApi { val groupInfo = chatModel.controller.apiNewGroup(groupProfile) @@ -59,7 +58,7 @@ fun AddGroupView(chatModel: ChatModel, close: () -> Unit) { } @Composable -fun AddGroupLayout(chatModelIncognito: Boolean, createGroup: (GroupProfile) -> Unit, close: () -> Unit) { +fun AddGroupLayout(createGroup: (GroupProfile) -> Unit, close: () -> Unit) { val bottomSheetModalState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden) val scope = rememberCoroutineScope() val displayName = rememberSaveable { mutableStateOf("") } @@ -91,14 +90,7 @@ fun AddGroupLayout(chatModelIncognito: Boolean, createGroup: (GroupProfile) -> U .padding(horizontal = DEFAULT_PADDING) ) { AppBarTitleCentered(stringResource(R.string.create_secret_group_title)) - Text(stringResource(R.string.group_is_decentralized), Modifier.fillMaxWidth(), textAlign = TextAlign.Center) - InfoAboutIncognito( - chatModelIncognito, - false, - generalGetString(R.string.group_unsupported_incognito_main_profile_sent), - generalGetString(R.string.group_main_profile_sent), - true - ) + ReadableText(R.string.group_is_decentralized, TextAlign.Center) Box( Modifier .fillMaxWidth() @@ -107,7 +99,7 @@ fun AddGroupLayout(chatModelIncognito: Boolean, createGroup: (GroupProfile) -> U ) { Box(contentAlignment = Alignment.TopEnd) { Box(contentAlignment = Alignment.Center) { - ProfileImage(size = 192.dp, image = profileImage.value) + ProfileImage(108.dp, image = profileImage.value) EditImageButton { scope.launch { bottomSheetModalState.show() } } } if (profileImage.value != null) { @@ -182,7 +174,6 @@ fun CreateGroupButton(color: Color, modifier: Modifier) { fun PreviewAddGroupLayout() { SimpleXTheme { AddGroupLayout( - chatModelIncognito = false, createGroup = {}, close = {} ) diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt index 27f3200fe2..7df26e2d2d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/onboarding/HowItWorks.kt @@ -60,6 +60,11 @@ fun ReadableText(@StringRes stringResId: Int, textAlign: TextAlign = TextAlign.S Text(annotatedStringResource(stringResId), modifier = Modifier.padding(padding), textAlign = textAlign, lineHeight = 22.sp) } +@Composable +fun ReadableText(text: String, textAlign: TextAlign = TextAlign.Start, padding: PaddingValues = PaddingValues(bottom = 12.dp)) { + Text(text, modifier = Modifier.padding(padding), textAlign = textAlign, lineHeight = 22.sp) +} + @Preview(showBackground = true) @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt index a00eb73420..f02c48eadb 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfileView.kt @@ -1,14 +1,10 @@ package chat.simplex.app.views.usersettings import android.content.res.Configuration -import android.graphics.Bitmap import android.net.Uri import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.BasicTextField -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* @@ -19,8 +15,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -40,10 +34,8 @@ import kotlinx.coroutines.launch fun UserProfileView(chatModel: ChatModel, close: () -> Unit) { val user = chatModel.currentUser.value if (user != null) { - val editProfile = rememberSaveable { mutableStateOf(false) } var profile by remember { mutableStateOf(user.profile.toProfile()) } UserProfileLayout( - editProfile = editProfile, profile = profile, close, saveProfile = { displayName, fullName, image -> @@ -53,7 +45,7 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) { chatModel.updateCurrentUser(newProfile) profile = newProfile } - editProfile.value = false + close() } } ) @@ -62,7 +54,6 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) { @Composable fun UserProfileLayout( - editProfile: MutableState, profile: Profile, close: () -> Unit, saveProfile: (String, String, String?) -> Unit, @@ -92,7 +83,19 @@ fun UserProfileLayout( sheetState = bottomSheetModalState, sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp) ) { - ModalView(close = close) { + val dataUnchanged = + displayName.value == profile.displayName && + fullName.value == profile.fullName && + chosenImage.value == null + + val closeWithAlert = { + if (dataUnchanged || !(displayName.value.isNotEmpty() && isValidDisplayName(displayName.value))) { + close() + } else { + showUnsavedChangesAlert({ saveProfile(displayName.value, fullName.value, profileImage.value) }, close) + } + } + ModalView(close = closeWithAlert) { Column( Modifier .verticalScroll(scrollState) @@ -100,99 +103,73 @@ fun UserProfileLayout( horizontalAlignment = Alignment.Start ) { AppBarTitleCentered(stringResource(R.string.your_current_profile)) - ReadableText(R.string.your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it, TextAlign.Center) - if (editProfile.value) { - Column( - Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.Start + val text = remember { + var t = generalGetString(R.string.your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it) + val index = t.indexOfFirst { it == '\n' } + if (index != -1) t = t.removeRange(index..index + 2) + t + } + ReadableText(text, TextAlign.Center) + Column( + Modifier + .fillMaxWidth() + ) { + Box( + Modifier + .fillMaxWidth() + .padding(bottom = 24.dp), + contentAlignment = Alignment.Center ) { - Box( - Modifier - .fillMaxWidth() - .padding(bottom = 24.dp), - contentAlignment = Alignment.Center - ) { - Box(contentAlignment = Alignment.TopEnd) { - Box(contentAlignment = Alignment.Center) { - ProfileImage(192.dp, profileImage.value) - EditImageButton { scope.launch { bottomSheetModalState.show() } } - } - if (profileImage.value != null) { - DeleteImageButton { profileImage.value = null } - } + Box(contentAlignment = Alignment.TopEnd) { + Box(contentAlignment = Alignment.Center) { + ProfileImage(108.dp, profileImage.value, color = HighOrLowlight.copy(alpha = 0.1f)) + EditImageButton { scope.launch { bottomSheetModalState.show() } } + } + if (profileImage.value != null) { + DeleteImageButton { profileImage.value = null } } } - Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text( - stringResource(R.string.display_name__field), - fontSize = 16.sp - ) - if (!isValidDisplayName(displayName.value)) { - Spacer(Modifier.size(DEFAULT_PADDING_HALF)) - Text( - stringResource(R.string.no_spaces), - fontSize = 16.sp, - color = Color.Red - ) - } - } - ProfileNameField(displayName, "", ::isValidDisplayName, focusRequester) - Spacer(Modifier.height(DEFAULT_PADDING)) + } + Row(Modifier.padding(bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { Text( - stringResource(R.string.full_name__field), - fontSize = 16.sp, - modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF) + stringResource(R.string.display_name__field), + fontSize = 16.sp ) - ProfileNameField(fullName) - Spacer(Modifier.height(DEFAULT_PADDING)) - Row { - TextButton(stringResource(R.string.cancel_verb)) { - displayName.value = profile.displayName - fullName.value = profile.fullName - profileImage.value = profile.image - editProfile.value = false - } - Spacer(Modifier.padding(horizontal = 8.dp)) - val enabled = displayName.value.isNotEmpty() && isValidDisplayName(displayName.value) - val saveModifier: Modifier - val saveColor: Color - if (enabled) { - saveModifier = Modifier - .clickable { saveProfile(displayName.value, fullName.value, profileImage.value) } - saveColor = MaterialTheme.colors.primary - } else { - saveModifier = Modifier - saveColor = HighOrLowlight - } + if (!isValidDisplayName(displayName.value)) { + Spacer(Modifier.size(DEFAULT_PADDING_HALF)) Text( - stringResource(R.string.save_and_notify_contacts), - modifier = saveModifier, - color = saveColor + stringResource(R.string.no_spaces), + fontSize = 16.sp, + color = Color.Red ) } } - } else { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.Start - ) { - Box( - Modifier - .fillMaxWidth() - .padding(bottom = 24.dp), contentAlignment = Alignment.Center - ) { - ProfileImage(192.dp, profile.image) - if (profile.image == null) { - EditImageButton { - editProfile.value = true - scope.launch { bottomSheetModalState.show() } - } - } - } - ProfileNameRow(stringResource(R.string.display_name__field), profile.displayName) - ProfileNameRow(stringResource(R.string.full_name__field), profile.fullName) - TextButton(stringResource(R.string.edit_verb)) { editProfile.value = true } + ProfileNameField(displayName, "", ::isValidDisplayName, focusRequester) + Spacer(Modifier.height(DEFAULT_PADDING)) + Text( + stringResource(R.string.full_name__field), + fontSize = 16.sp, + modifier = Modifier.padding(bottom = DEFAULT_PADDING_HALF) + ) + ProfileNameField(fullName) + + Spacer(Modifier.height(DEFAULT_PADDING)) + val enabled = !dataUnchanged && displayName.value.isNotEmpty() && isValidDisplayName(displayName.value) + val saveModifier: Modifier + val saveColor: Color + if (enabled) { + saveModifier = Modifier + .clickable { saveProfile(displayName.value, fullName.value, profileImage.value) } + saveColor = MaterialTheme.colors.primary + } else { + saveModifier = Modifier + saveColor = HighOrLowlight } + Text( + stringResource(R.string.save_and_notify_contacts), + modifier = saveModifier, + color = saveColor + ) } Spacer(Modifier.height(DEFAULT_BOTTOM_BUTTON_PADDING)) if (savedKeyboardState != keyboardState) { @@ -209,60 +186,17 @@ fun UserProfileLayout( } } -@Composable -fun ProfileNameTextField(name: MutableState) { - BasicTextField( - value = name.value, - onValueChange = { name.value = it }, - modifier = Modifier - .padding(bottom = 24.dp) - .padding(start = 28.dp) - .fillMaxWidth(), - textStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground), - keyboardOptions = KeyboardOptions( - capitalization = KeyboardCapitalization.None, - autoCorrect = false - ), - singleLine = true - ) -} - -@Composable -fun ProfileNameRow(label: String, text: String) { - Row(Modifier.padding(bottom = 24.dp)) { - Text( - label, - color = MaterialTheme.colors.onBackground - ) - Spacer(Modifier.padding(horizontal = 4.dp)) - Text( - text, - fontWeight = FontWeight.Bold, - color = MaterialTheme.colors.onBackground - ) - } -} - -@Composable -fun TextButton(text: String, click: () -> Unit) { - Text( - text, - color = MaterialTheme.colors.primary, - modifier = Modifier.clickable(onClick = click), - ) -} - @Composable fun EditImageButton(click: () -> Unit) { IconButton( onClick = click, - modifier = Modifier.background(Color(1f, 1f, 1f, 0.2f), shape = CircleShape) + modifier = Modifier.size(30.dp) ) { Icon( Icons.Outlined.PhotoCamera, contentDescription = stringResource(R.string.edit_image), tint = MaterialTheme.colors.primary, - modifier = Modifier.size(36.dp) + modifier = Modifier.size(30.dp) ) } } @@ -278,6 +212,16 @@ fun DeleteImageButton(click: () -> Unit) { } } +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, + ) +} + @Preview(showBackground = true) @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, @@ -290,7 +234,6 @@ fun PreviewUserProfileLayoutEditOff() { UserProfileLayout( profile = Profile.sampleData, close = {}, - editProfile = remember { mutableStateOf(false) }, saveProfile = { _, _, _ -> } ) } @@ -308,7 +251,6 @@ fun PreviewUserProfileLayoutEditOn() { UserProfileLayout( profile = Profile.sampleData, close = {}, - editProfile = remember { mutableStateOf(true) }, saveProfile = { _, _, _ -> } ) }