From 45a8da004b121c4381b29a74295a7caeddf1e865 Mon Sep 17 00:00:00 2001 From: Emin Date: Fri, 21 Jun 2024 10:53:58 +0500 Subject: [PATCH] do profile UI/UX --- .../java/app/tourism/data/dto/profile/User.kt | 10 +- .../java/app/tourism/ui/common/LoadImage.kt | 12 -- .../ui/common/buttons/SecondaryButton.kt | 75 +++++++++ .../ui/common/textfields/AppEditText.kt | 6 +- .../tourism/ui/screens/main/MainNavigation.kt | 30 ++-- .../tourism/ui/screens/main/MainSection.kt | 10 +- .../personal_data/PersonalDataScreen.kt | 158 +++++++++++++++++- .../main/profile/profile/ProfileScreen.kt | 109 ++++++++++-- .../main/profile/profile/ProfileViewModel.kt | 47 ++++++ .../app/src/main/res/layout/ccp_profile.xml | 2 +- 10 files changed, 391 insertions(+), 68 deletions(-) create mode 100644 android/app/src/main/java/app/tourism/ui/common/buttons/SecondaryButton.kt diff --git a/android/app/src/main/java/app/tourism/data/dto/profile/User.kt b/android/app/src/main/java/app/tourism/data/dto/profile/User.kt index 7fed946a56..d15f119793 100644 --- a/android/app/src/main/java/app/tourism/data/dto/profile/User.kt +++ b/android/app/src/main/java/app/tourism/data/dto/profile/User.kt @@ -3,12 +3,10 @@ package app.tourism.data.dto.profile data class User( val id: Int, val avatar: String?, - val country_id: Any?, - val created_at: String, + val country: String, val full_name: String, - val language: Int, - val phone: String, - val theme: Int, - val updated_at: String, + val language: String, + val phone: String?, + val theme: String, val username: String ) \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/LoadImage.kt b/android/app/src/main/java/app/tourism/ui/common/LoadImage.kt index 5ab77c597d..f98ad52041 100644 --- a/android/app/src/main/java/app/tourism/ui/common/LoadImage.kt +++ b/android/app/src/main/java/app/tourism/ui/common/LoadImage.kt @@ -54,7 +54,6 @@ fun CoilImg( modifier = modifier.background(color = backgroundColor), model = ImageRequest.Builder(LocalContext.current) .data(url) - .decoderFactory(SvgDecoder.Factory()) .crossfade(500) .error(R.drawable.error_centered) .build(), @@ -63,14 +62,3 @@ fun CoilImg( contentScale = contentScale ) } - -@Composable -fun CoilImgAccompanist() { - // CoilImage( -// modifier = modifier, -// imageModel = url, -// contentScale = contentScale, -// placeHolder = painterResource(R.drawable.placeholder), -// error = painterResource(R.drawable.error_centered) -// ) -} diff --git a/android/app/src/main/java/app/tourism/ui/common/buttons/SecondaryButton.kt b/android/app/src/main/java/app/tourism/ui/common/buttons/SecondaryButton.kt new file mode 100644 index 0000000000..271438e4a2 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/buttons/SecondaryButton.kt @@ -0,0 +1,75 @@ +package app.tourism.ui.common.buttons + +import ButtonLoading +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.organicmaps.R +import app.tourism.ui.common.HorizontalSpace +import app.tourism.ui.theme.TextStyles + +@Composable +fun SecondaryButton( + modifier: Modifier = Modifier, + label: String, + loading: Boolean = false, + icon: (@Composable () -> Unit)? = null, + onClick: () -> Unit +) { + val shape = RoundedCornerShape(16.dp) + + Box( + modifier = Modifier + .height(height = 56.dp) + .background(color = colorResource(id = R.color.transparent), shape = shape) + .border(width = 1.dp, color = MaterialTheme.colorScheme.primary, shape = shape) + .clip(shape) + .clickable { onClick() } + .then(modifier), + ) { + Row( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + if (!loading) { + icon?.apply { + invoke() + HorizontalSpace(width = 8.dp) + } + Text( + text = label, + style = TextStyles.h4, + textAlign = TextAlign.Center, + fontSize = 16.sp, + fontWeight = FontWeight.W600, + color = MaterialTheme.colorScheme.primary + ) + } else { + ButtonLoading() + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt b/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt index 85e7cf4f94..ed961ea317 100644 --- a/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt +++ b/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt @@ -7,9 +7,11 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.res.colorResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import app.organicmaps.R import app.tourism.ui.theme.TextStyles @Composable @@ -25,7 +27,7 @@ fun AppEditText( value = value, onValueChange = onValueChange, hint = hint, - hintColor = Color.Gray, + hintColor = MaterialTheme.colorScheme.onBackground, isError = isError, textFieldHeight = 50.dp, textFieldPadding = PaddingValues(vertical = 8.dp), @@ -39,7 +41,7 @@ fun AppEditText( keyboardActions = keyboardActions, cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), focusedColor = MaterialTheme.colorScheme.onBackground, - unfocusedColor = Color.Gray, + unfocusedColor = MaterialTheme.colorScheme.onBackground, errorColor = MaterialTheme.colorScheme.onError ) } diff --git a/android/app/src/main/java/app/tourism/ui/screens/main/MainNavigation.kt b/android/app/src/main/java/app/tourism/ui/screens/main/MainNavigation.kt index 5854dc0239..a722707ca8 100644 --- a/android/app/src/main/java/app/tourism/ui/screens/main/MainNavigation.kt +++ b/android/app/src/main/java/app/tourism/ui/screens/main/MainNavigation.kt @@ -5,6 +5,7 @@ import android.content.Intent import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat +import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -18,24 +19,12 @@ import app.tourism.ui.screens.main.favorites.favorites.FavoritesScreen import app.tourism.ui.screens.main.home.home.HomeScreen import app.tourism.ui.screens.main.profile.personal_data.PersonalDataScreen import app.tourism.ui.screens.main.profile.profile.ProfileScreen +import app.tourism.ui.screens.main.profile.profile.ProfileViewModel import app.tourism.ui.screens.main.site_details.SiteDetailsScreen import app.tourism.utils.navigateToMap import app.tourism.utils.navigateToMapForRoute import kotlinx.serialization.Serializable -// tabs -@Serializable -object HomeTab - -@Serializable -object CategoriesTab - -@Serializable -object FavoritesTab - -@Serializable -object ProfileTab - // home @Serializable object Home @@ -71,17 +60,17 @@ fun MainNavigation(rootNavController: NavHostController, themeVM: ThemeViewModel } val onMapClick = { navigateToMap(context) } - NavHost(rootNavController, startDestination = HomeTab) { - composable { + NavHost(rootNavController, startDestination = "home_tab") { + composable("home_tab") { HomeNavHost(onSiteClick, onMapClick) } - composable { + composable("categories_tab") { CategoriesNavHost(onSiteClick, onMapClick) } - composable { + composable("favorites_tab") { FavoritesNavHost(onSiteClick) } - composable { + composable("profile_tab") { ProfileNavHost(themeVM = themeVM) } composable { backStackEntry -> @@ -139,7 +128,7 @@ fun FavoritesNavHost(onSiteClick: (id: Int) -> Unit) { } @Composable -fun ProfileNavHost(themeVM: ThemeViewModel) { +fun ProfileNavHost(themeVM: ThemeViewModel, profileVM: ProfileViewModel = hiltViewModel()) { val context = LocalContext.current val profileNavController = rememberNavController() val onBackClick = { profileNavController.navigateUp() } @@ -156,11 +145,12 @@ fun ProfileNavHost(themeVM: ThemeViewModel) { onSignOutComplete = { navigateToAuth(context) }, + profileVM = profileVM, themeVM = themeVM ) } composable { - PersonalDataScreen(onBackClick) + PersonalDataScreen(onBackClick, profileVM) } composable { LanguageScreen(onBackClick) diff --git a/android/app/src/main/java/app/tourism/ui/screens/main/MainSection.kt b/android/app/src/main/java/app/tourism/ui/screens/main/MainSection.kt index b917170fd9..8c807b4395 100644 --- a/android/app/src/main/java/app/tourism/ui/screens/main/MainSection.kt +++ b/android/app/src/main/java/app/tourism/ui/screens/main/MainSection.kt @@ -102,7 +102,7 @@ fun MainSection(themeVM: ThemeViewModel) { } data class BottomNavigationItem( - val route: Any, + val route: String, val title: String, @DrawableRes val unselectedIcon: Int, @DrawableRes val selectedIcon: Int @@ -112,25 +112,25 @@ data class BottomNavigationItem( fun getNavItems(): List { return listOf( BottomNavigationItem( - route = HomeTab, + route = "home_tab", title = stringResource(id = R.string.home), selectedIcon = R.drawable.home_selected, unselectedIcon = R.drawable.home, ), BottomNavigationItem( - route = CategoriesTab, + route = "categories_tab", title = stringResource(id = R.string.categories), selectedIcon = R.drawable.categories_selected, unselectedIcon = R.drawable.categories, ), BottomNavigationItem( - route = FavoritesTab, + route = "favorites_tab", title = stringResource(id = R.string.favorites), selectedIcon = R.drawable.heart_selected, unselectedIcon = R.drawable.heart, ), BottomNavigationItem( - route = ProfileTab, + route = "profile_tab", title = stringResource(id = R.string.profile_tourism), selectedIcon = R.drawable.profile_selected, unselectedIcon = R.drawable.profile, diff --git a/android/app/src/main/java/app/tourism/ui/screens/main/profile/personal_data/PersonalDataScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/main/profile/personal_data/PersonalDataScreen.kt index 89ff1ef689..485bf687f7 100644 --- a/android/app/src/main/java/app/tourism/ui/screens/main/profile/personal_data/PersonalDataScreen.kt +++ b/android/app/src/main/java/app/tourism/ui/screens/main/profile/personal_data/PersonalDataScreen.kt @@ -1,26 +1,176 @@ package app.tourism.ui.screens.main.profile.personal_data +import android.view.LayoutInflater +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.viewinterop.AndroidView import app.organicmaps.R +import app.tourism.Constants +import app.tourism.domain.models.resource.Resource +import app.tourism.ui.ObserveAsEvents +import app.tourism.ui.common.HorizontalSpace +import app.tourism.ui.common.LoadImg +import app.tourism.ui.common.SpaceForNavBar +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton import app.tourism.ui.common.nav.AppTopBar +import app.tourism.ui.common.textfields.AppEditText +import app.tourism.ui.screens.main.profile.profile.ProfileViewModel +import app.tourism.ui.screens.main.profile.profile.UiEvent +import app.tourism.ui.theme.TextStyles +import app.tourism.ui.utils.showToast +import com.hbb20.CountryCodePicker @Composable -fun PersonalDataScreen(onBackClick: () -> Boolean) { +fun PersonalDataScreen(onBackClick: () -> Boolean, profileVM: ProfileViewModel) { + val context = LocalContext.current + val focusManager = LocalFocusManager.current + + val personalData = profileVM.profileDataResource.collectAsState().value + val fullName = profileVM.fullName.collectAsState().value + val email = profileVM.email.collectAsState().value + val countryCodeName = profileVM.countryCodeName.collectAsState().value + + ObserveAsEvents(flow = profileVM.uiEventsChannelFlow) { event -> + if (event is UiEvent.ShowToast) context.showToast(event.message) + } + Scaffold( topBar = { AppTopBar( title = stringResource(id = R.string.personal_data), onBackClick = onBackClick, ) - } + }, + contentWindowInsets = Constants.USUAL_WINDOW_INSETS ) { paddingValues -> - Column(Modifier.padding(paddingValues)) { - // todo + if (personalData is Resource.Success && personalData.data != null) { + val data = personalData.data + Column( + Modifier + .padding(paddingValues) + .verticalScroll(rememberScrollState()) + ) { + VerticalSpace(height = 32.dp) + Row( + Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + LoadImg( + modifier = Modifier + .size(100.dp) + .clip(CircleShape), + url = data.pfpUrl + ) + HorizontalSpace(width = 20.dp) + Row( + modifier = Modifier + .clickable { + + } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + val uploadPhotoText = stringResource(id = R.string.upload_photo) + Icon( + painter = painterResource(id = R.drawable.image_down), + contentDescription = uploadPhotoText, + ) + HorizontalSpace(width = 8.dp) + Text(text = uploadPhotoText, style = TextStyles.h4) + } + } + VerticalSpace(height = 24.dp) + + AppEditText( + value = fullName, onValueChange = { profileVM.setFullName(it) }, + hint = stringResource(id = R.string.full_name), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + SpaceBetweenTextFields() + + AppEditText( + value = email, onValueChange = { profileVM.setEmail(it) }, + hint = stringResource(id = R.string.email), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + SpaceBetweenTextFields() + + Text( + text = stringResource(id = R.string.country), + fontSize = 12.sp + ) + AndroidView( + factory = { context -> + val view = LayoutInflater.from(context) + .inflate(R.layout.ccp_profile, null, false) + val ccp = view.findViewById(R.id.ccp) + ccp.setCountryForNameCode(countryCodeName) + ccp.setOnCountryChangeListener { + profileVM.setCountryCodeName(ccp.selectedCountryNameCode) + } + view + } + ) + VerticalSpace(height = 10.dp) + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.onBackground, + thickness = 1.dp + ) + VerticalSpace(height = 48.dp) + + PrimaryButton( + label = stringResource(id = R.string.save), + onClick = { profileVM.save() }, + ) + + SpaceForNavBar() + } } } +} + +@Composable +fun ColumnScope.SpaceBetweenTextFields() { + VerticalSpace(height = 24.dp) } \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileScreen.kt index ee6a1943f3..560e23698f 100644 --- a/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileScreen.kt +++ b/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileScreen.kt @@ -10,22 +10,32 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import androidx.hilt.navigation.compose.hiltViewModel @@ -40,6 +50,8 @@ import app.tourism.ui.common.HorizontalSpace import app.tourism.ui.common.LoadImg import app.tourism.ui.common.SpaceForNavBar import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton +import app.tourism.ui.common.buttons.SecondaryButton import app.tourism.ui.common.nav.AppTopBar import app.tourism.ui.common.ui_state.Loading import app.tourism.ui.screens.main.ThemeViewModel @@ -47,19 +59,20 @@ import app.tourism.ui.theme.TextStyles import app.tourism.ui.utils.showToast import com.hbb20.CountryCodePicker +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ProfileScreen( onPersonalDataClick: () -> Unit, onLanguageClick: () -> Unit, onSignOutComplete: () -> Unit, - vm: ProfileViewModel = hiltViewModel(), + profileVM: ProfileViewModel, themeVM: ThemeViewModel, ) { val context = LocalContext.current - val personalData = vm.profileDataResource.collectAsState().value - val signOutResponse = vm.signOutResponse.collectAsState().value + val personalData = profileVM.profileDataResource.collectAsState().value + val signOutResponse = profileVM.signOutResponse.collectAsState().value - ObserveAsEvents(flow = vm.uiEventsChannelFlow) { event -> + ObserveAsEvents(flow = profileVM.uiEventsChannelFlow) { event -> when (event) { is UiEvent.NavigateToAuth -> onSignOutComplete() is UiEvent.ShowToast -> context.showToast(event.message) @@ -79,53 +92,74 @@ fun ProfileScreen( .padding(paddingValues) .verticalScroll(rememberScrollState()) ) { + VerticalSpace(height = 32.dp) if (personalData is Resource.Success) { personalData.data?.let { - ProfileBar(it, onPersonalDataClick) + ProfileBar(it) + VerticalSpace(height = 32.dp) } } - VerticalSpace(height = 48.dp) // todo currency rates. Couldn't find free api or library :( CurrencyRates(currencyRates = CurrencyRates(10.88, 10.88, 10.88)) - VerticalSpace(height = 24.dp) + VerticalSpace(height = 20.dp) GenericProfileItem( label = stringResource(R.string.personal_data), icon = R.drawable.profile, onClick = onPersonalDataClick ) - VerticalSpace(height = 24.dp) + VerticalSpace(height = 20.dp) GenericProfileItem( label = stringResource(R.string.language), icon = R.drawable.globe, onClick = onLanguageClick ) - VerticalSpace(height = 24.dp) + VerticalSpace(height = 20.dp) ThemeSwitch(themeVM = themeVM) - VerticalSpace(height = 24.dp) + VerticalSpace(height = 20.dp) + val sheetState = rememberModalBottomSheetState() + var isSheetOpen by rememberSaveable { mutableStateOf(false) } GenericProfileItem( label = stringResource(R.string.sign_out), icon = R.drawable.sign_out, isLoading = signOutResponse is Resource.Loading, - onClick = { vm.signOut() } + onClick = { isSheetOpen = true } ) + + if (isSheetOpen) { + ModalBottomSheet( + containerColor = MaterialTheme.colorScheme.background, + sheetState = sheetState, + onDismissRequest = { + isSheetOpen = false + }, + ) { + SignOutWarning( + onSignOutClick = { profileVM.signOut() }, + onCancelClick = { isSheetOpen = false }, + ) + } + } + SpaceForNavBar() } } } @Composable -fun ProfileBar(personalData: PersonalData, onPersonalDataClick: () -> Unit) { +fun ProfileBar(personalData: PersonalData) { Row( - Modifier - .fillMaxWidth() - .clickable { onPersonalDataClick() }, + Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - LoadImg(url = personalData.pfpUrl) + LoadImg( + modifier = Modifier + .size(100.dp) + .clip(CircleShape), + url = personalData.pfpUrl + ) HorizontalSpace(width = 16.dp) Column { Text(text = personalData.fullName, style = TextStyles.h2) - VerticalSpace(height = 16.dp) Country(Modifier.fillMaxWidth(), personalData.country) } } @@ -179,7 +213,7 @@ fun CurrencyRatesItem(currency: String, value: String) { Row { Text(text = currency, style = TextStyles.b1) HorizontalSpace(width = 4.dp) - Text(text = value, style = TextStyles.b1.copy(fontWeight = FontWeight.Medium)) + Text(text = value, style = TextStyles.b1.copy(fontWeight = FontWeight.SemiBold)) } } @@ -237,3 +271,42 @@ fun ThemeSwitch(modifier: Modifier = Modifier, themeVM: ThemeViewModel) { ) } } + +@Composable +fun SignOutWarning( + modifier: Modifier = Modifier, + onSignOutClick: () -> Unit, + onCancelClick: () -> Unit, +) { + Column( + Modifier + .padding(top = 0.dp, bottom = 48.dp, start = 32.dp, end = 32.dp) + .then(modifier), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(id = R.string.sign_out_title), + style = TextStyles.h3.copy(fontWeight = FontWeight.W700) + ) + VerticalSpace(height = 24.dp) + Text( + text = stringResource(id = R.string.sign_out_warning), + style = TextStyles.h3.copy(fontWeight = FontWeight.W500), + textAlign = TextAlign.Center + ) + VerticalSpace(height = 32.dp) + Row { + SecondaryButton( + modifier = Modifier.weight(1f), + label = stringResource(id = R.string.cancel), + onClick = onCancelClick, + ) + HorizontalSpace(width = 16.dp) + PrimaryButton( + modifier = Modifier.weight(1f), + label = stringResource(id = R.string.sign_out), + onClick = onSignOutClick, + ) + } + } +} diff --git a/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileViewModel.kt b/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileViewModel.kt index 92e9f9840a..d7d7f2a501 100644 --- a/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileViewModel.kt +++ b/android/app/src/main/java/app/tourism/ui/screens/main/profile/profile/ProfileViewModel.kt @@ -26,6 +26,32 @@ class ProfileViewModel @Inject constructor( private val uiChannel = Channel() val uiEventsChannelFlow = uiChannel.receiveAsFlow() + // region fields to update + private val _fullName = MutableStateFlow("") + val fullName = _fullName.asStateFlow() + + fun setFullName(value: String) { + _fullName.value = value + } + + + private val _email = MutableStateFlow("") + val email = _email.asStateFlow() + + fun setEmail(value: String) { + _email.value = value + } + + + private val _countryCodeName = MutableStateFlow(null) + val countryCodeName = _countryCodeName.asStateFlow() + + fun setCountryCodeName(value: String) { + _countryCodeName.value = value + } + // endregion fields to update + + // region requests private val _personalDataResource = MutableStateFlow>(Resource.Idle()) val profileDataResource = _personalDataResource.asStateFlow() @@ -34,6 +60,9 @@ class ProfileViewModel @Inject constructor( profileRepository.getPersonalData() .collectLatest { resource -> _personalDataResource.value = resource + if (resource is Resource.Success) { + resource.data?.let { updatePersonalDataInMemory(it) } + } if (resource is Resource.Error) { uiChannel.send(UiEvent.ShowToast(resource.message ?: "")) } @@ -41,6 +70,23 @@ class ProfileViewModel @Inject constructor( } } + + fun save() { + viewModelScope.launch { + // todo + } + } + + + private fun updatePersonalDataInMemory(personalData: PersonalData) { + personalData.let { + setFullName(it.fullName) + setEmail(it.email) + setCountryCodeName(it.country) + } + } + + private val _signOutResponse = MutableStateFlow>(Resource.Idle()) val signOutResponse = _signOutResponse.asStateFlow() @@ -60,6 +106,7 @@ class ProfileViewModel @Inject constructor( } } } + // endregion requests init { getPersonalData() diff --git a/android/app/src/main/res/layout/ccp_profile.xml b/android/app/src/main/res/layout/ccp_profile.xml index 4adbe2e667..8f80c47c6b 100644 --- a/android/app/src/main/res/layout/ccp_profile.xml +++ b/android/app/src/main/res/layout/ccp_profile.xml @@ -4,7 +4,7 @@ android:id="@+id/ccp" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingVertical="12dp" + android:paddingVertical="0dp" app:ccp_autoDetectLanguage="true" app:ccp_contentColor="@color/onBackground" app:ccpDialog_backgroundColor="@color/transparent"