From c130905e9a2fef07d270f690d697ee36f8d795f8 Mon Sep 17 00:00:00 2001 From: "omer.habib" Date: Thu, 23 Jan 2025 01:01:12 +0500 Subject: [PATCH 1/3] fix: Bugs with navigation and UI improvements on EditProfile - Handle onBackPressed - Invalidate `Done` button on data change on EditProfile - Add Loader while hitting API fix: LEARNER-10388 --- .../presentation/edit/EditProfileFragment.kt | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt index 32c2372d7..fa565b7ff 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt @@ -18,6 +18,7 @@ import android.provider.MediaStore import android.view.Gravity import android.view.LayoutInflater import android.view.ViewGroup +import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -43,6 +44,7 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.Card +import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.ContentAlpha import androidx.compose.material.Divider import androidx.compose.material.ExperimentalMaterialApi @@ -163,6 +165,17 @@ class EditProfileFragment : Fragment() { } } + private val onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (viewModel.profileDataChanged) { + viewModel.setShowLeaveDialog(true) + } else { + viewModel.setShowLeaveDialog(false) + requireActivity().supportFragmentManager.popBackStackImmediate() + } + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -241,6 +254,16 @@ class EditProfileFragment : Fragment() { } } + override fun onResume() { + super.onResume() + activity?.onBackPressedDispatcher?.addCallback(onBackPressedCallback) + } + + override fun onPause() { + onBackPressedCallback.remove() + super.onPause() + } + @Suppress("DEPRECATION") private fun cropImage(uri: Uri): Uri { val matrix = Matrix() @@ -360,7 +383,7 @@ private fun EditProfileScreen( mutableStateMapOf( Pair( YEAR_OF_BIRTH, - if (uiState.account.yearOfBirth != null) uiState.account.yearOfBirth.toString() else "" + if (uiState.account.yearOfBirth != null) uiState.account.yearOfBirth.toString() else null ), Pair(LANGUAGE, uiState.account.languageProficiencies), Pair(COUNTRY, uiState.account.country), @@ -598,7 +621,7 @@ private fun EditProfileScreen( ) { Box( modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.BottomCenter + contentAlignment = Alignment.Center ) { Column( Modifier @@ -777,6 +800,13 @@ private fun EditProfileScreen( ) Spacer(Modifier.height(52.dp)) } + if (uiState.isUpdating) { + CircularProgressIndicator( + modifier = Modifier + .size(42.dp), + color = MaterialTheme.appColors.primary, + ) + } if (openWarningMessageDialog) { LimitedProfileDialog( modifier = popUpModifier @@ -1003,7 +1033,7 @@ private fun SelectableField( unfocusedBorderColor = MaterialTheme.appColors.textFieldBorder, cursorColor = MaterialTheme.appColors.textFieldText, disabledBorderColor = MaterialTheme.appColors.textFieldBorder, - disabledTextColor = MaterialTheme.appColors.textFieldHint, + disabledTextColor = MaterialTheme.appColors.textPrimary, disabledPlaceholderColor = MaterialTheme.appColors.textFieldHint ) } From ff152eec95cb892ed4db2be187072249c21bcb8a Mon Sep 17 00:00:00 2001 From: "omer.habib" Date: Mon, 27 Jan 2025 20:47:02 +0500 Subject: [PATCH 2/3] fix: Address PR comments - 1 --- profile/build.gradle | 3 +- .../presentation/edit/EditProfileFragment.kt | 63 +++++++++++-------- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/profile/build.gradle b/profile/build.gradle index 2ccd98e63..6a78d445d 100644 --- a/profile/build.gradle +++ b/profile/build.gradle @@ -56,6 +56,7 @@ android { dependencies { implementation project(path: ":core") + implementation 'androidx.activity:activity-compose:1.8.1' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' @@ -64,4 +65,4 @@ dependencies { testImplementation "io.mockk:mockk-android:$mockk_version" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" testImplementation "androidx.arch.core:core-testing:$android_arch_version" -} \ No newline at end of file +} diff --git a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt index fa565b7ff..7c328cdf7 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt @@ -19,6 +19,7 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.ViewGroup import androidx.activity.OnBackPressedCallback +import androidx.activity.compose.LocalOnBackPressedDispatcherOwner import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -66,6 +67,7 @@ import androidx.compose.material.icons.outlined.Report import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -165,17 +167,6 @@ class EditProfileFragment : Fragment() { } } - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - if (viewModel.profileDataChanged) { - viewModel.setShowLeaveDialog(true) - } else { - viewModel.setShowLeaveDialog(false) - requireActivity().supportFragmentManager.popBackStackImmediate() - } - } - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -250,18 +241,35 @@ class EditProfileFragment : Fragment() { viewModel.isLimitedProfile = it } ) + + HandleBackNavigation() } } } - override fun onResume() { - super.onResume() - activity?.onBackPressedDispatcher?.addCallback(onBackPressedCallback) - } + @Composable + private fun HandleBackNavigation() { + val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher + + val onBackPressedCallback = remember { + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (viewModel.profileDataChanged) { + viewModel.setShowLeaveDialog(true) + } else { + viewModel.setShowLeaveDialog(false) + requireActivity().supportFragmentManager.popBackStackImmediate() + } + } + } + } - override fun onPause() { - onBackPressedCallback.remove() - super.onPause() + DisposableEffect(backDispatcher) { + backDispatcher?.addCallback(onBackPressedCallback) + onDispose { + onBackPressedCallback.remove() + } + } } @Suppress("DEPRECATION") @@ -381,10 +389,7 @@ private fun EditProfileScreen( val mapFields = rememberSaveableMap { mutableStateMapOf( - Pair( - YEAR_OF_BIRTH, - if (uiState.account.yearOfBirth != null) uiState.account.yearOfBirth.toString() else null - ), + Pair(YEAR_OF_BIRTH, uiState.account.yearOfBirth?.toString()), Pair(LANGUAGE, uiState.account.languageProficiencies), Pair(COUNTRY, uiState.account.country), Pair(BIO, uiState.account.bio), @@ -392,7 +397,7 @@ private fun EditProfileScreen( ) } - val saveButtonEnabled = !(uiState.account.yearOfBirth.toString() == mapFields[YEAR_OF_BIRTH] + val saveButtonEnabled = !(uiState.account.yearOfBirth?.toString() == mapFields[YEAR_OF_BIRTH] && uiState.account.languageProficiencies == mapFields[LANGUAGE] && uiState.account.country == mapFields[COUNTRY] && uiState.account.bio == mapFields[BIO] @@ -1021,6 +1026,8 @@ private fun SelectableField( disabled: Boolean = false, onClick: () -> Unit, ) { + val focusManager = LocalFocusManager.current + val colors = if (disabled) { TextFieldDefaults.outlinedTextFieldColors( unfocusedBorderColor = MaterialTheme.appColors.textFieldBorder, @@ -1069,14 +1076,18 @@ private fun SelectableField( .testTag("tf_select_${name.tagId()}") .fillMaxWidth() .noRippleClickable { - if (!disabled) + if (!disabled) { onClick() + focusManager.clearFocus() + } }, placeholder = { Text( - modifier = Modifier.testTag("txt_placeholder_${name.tagId()}"), + modifier = Modifier + .alpha(if (disabled) ContentAlpha.disabled else ContentAlpha.high) + .testTag("txt_placeholder_${name.tagId()}"), text = name, - color = MaterialTheme.appColors.textFieldText, + color = MaterialTheme.appColors.textFieldHint, style = MaterialTheme.appTypography.bodyMedium ) } From b6a6ae88e49e3fd74974cd44435dd63ac457f728 Mon Sep 17 00:00:00 2001 From: "omer.habib" Date: Wed, 29 Jan 2025 12:16:28 +0530 Subject: [PATCH 3/3] fix: Address PR comments - 2 --- .../presentation/edit/EditProfileFragment.kt | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt index 7c328cdf7..69b79a790 100644 --- a/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt +++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt @@ -197,12 +197,7 @@ class EditProfileFragment : Fragment() { isImageDeleted = isImageDeleted, leaveDialog = leaveDialog, onBackClick = { - if (it) { - viewModel.setShowLeaveDialog(true) - } else { - viewModel.setShowLeaveDialog(false) - requireActivity().supportFragmentManager.popBackStackImmediate() - } + onBackPressed(it) }, onSaveClick = { fields -> viewModel.profileEditDoneClickedEvent() @@ -254,12 +249,7 @@ class EditProfileFragment : Fragment() { val onBackPressedCallback = remember { object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - if (viewModel.profileDataChanged) { - viewModel.setShowLeaveDialog(true) - } else { - viewModel.setShowLeaveDialog(false) - requireActivity().supportFragmentManager.popBackStackImmediate() - } + onBackPressed(viewModel.profileDataChanged) } } } @@ -272,6 +262,15 @@ class EditProfileFragment : Fragment() { } } + private fun onBackPressed(showDialog: Boolean) { + if (showDialog) { + viewModel.setShowLeaveDialog(true) + } else { + viewModel.setShowLeaveDialog(false) + requireActivity().supportFragmentManager.popBackStackImmediate() + } + } + @Suppress("DEPRECATION") private fun cropImage(uri: Uri): Uri { val matrix = Matrix()