From d3574883bf5a6592a17ab156b7a0e61a63c54978 Mon Sep 17 00:00:00 2001 From: Sk Niyaj Ali Date: Mon, 8 Apr 2024 23:24:23 +0530 Subject: [PATCH 1/2] Enhancement - ProfileScreen UI - Close #210 --- .../niyaj/feature/profile/ProfileScreen.kt | 73 +++--- .../niyaj/feature/profile/ProfileViewModel.kt | 25 +- .../profile/components/RestaurantCard.kt | 224 ++++++++++++++++-- 3 files changed, 260 insertions(+), 62 deletions(-) diff --git a/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileScreen.kt b/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileScreen.kt index f9b95a08..e0445ba7 100644 --- a/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileScreen.kt +++ b/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileScreen.kt @@ -13,11 +13,14 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.FabPosition import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.Scaffold import androidx.compose.material.ScaffoldState +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Password import androidx.compose.runtime.Composable @@ -29,6 +32,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController @@ -44,12 +48,9 @@ import com.niyaj.feature.profile.components.RestaurantCard import com.niyaj.feature.profile.destinations.UpdateProfileScreenDestination import com.niyaj.model.RESTAURANT_LOGO_NAME import com.niyaj.model.RESTAURANT_PRINT_LOGO_NAME -import com.niyaj.ui.components.ScrollToTop import com.niyaj.ui.components.SettingsCard -import com.niyaj.ui.components.StandardScaffoldNew import com.niyaj.ui.event.UiEvent import com.niyaj.ui.util.Screens -import com.niyaj.ui.util.isScrolled import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate @@ -179,39 +180,43 @@ fun ProfileScreen( } } - - StandardScaffoldNew( - navController = navController, + Scaffold( + modifier = Modifier + .fillMaxSize(), scaffoldState = scaffoldState, - showBackButton = true, - selectionCount = 0, - title = PROFILE_SCREEN, - navActions = { - IconButton( - onClick = { - navController.navigate(UpdateProfileScreenDestination()) - } - ) { - Icon(imageVector = Icons.Default.Edit, contentDescription = "Edit Profile") - } - }, - showFab = true, - floatingActionButton = { - ScrollToTop( - visible = lazyListState.isScrolled, - onClick = { - scope.launch { - lazyListState.animateScrollToItem(index = 0) + topBar = { + TopAppBar( + title = { Text(text = PROFILE_SCREEN) }, + navigationIcon = { + IconButton( + onClick = { navController.navigateUp() } + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Navigate Back" + ) } }, + actions = { + IconButton( + onClick = { + navController.navigate(UpdateProfileScreenDestination()) + } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "Edit Profile" + ) + } + }, + elevation = 0.dp ) - }, - fabPosition = FabPosition.End, + } ) { LazyColumn( modifier = Modifier .fillMaxSize() - .padding(SpaceSmall) + .padding(it) .background(LightColor6), state = lazyListState, verticalArrangement = Arrangement.spacedBy(SpaceSmall) @@ -241,11 +246,15 @@ fun ProfileScreen( } item("Account Info") { - AccountInfo(account = accountInfo) + AccountInfo( + modifier = Modifier.padding(SpaceSmall), + account = accountInfo + ) } item("Change Password") { SettingsCard( + modifier = Modifier.padding(SpaceSmall), text = "Change Password", icon = Icons.Default.Password ) { @@ -256,6 +265,4 @@ fun ProfileScreen( } } } -} - - +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileViewModel.kt b/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileViewModel.kt index 0d5399d7..d865eae8 100644 --- a/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileViewModel.kt +++ b/feature/profile/src/main/java/com/niyaj/feature/profile/ProfileViewModel.kt @@ -158,26 +158,21 @@ class ProfileViewModel @Inject constructor( val validatedName = validation.validateRestaurantName(updateState.name) val validatedTagLine = validation.validateRestaurantTagline(updateState.tagline) val validatedEmail = validation.validateRestaurantEmail(updateState.email) - val validatedPrimaryPhone = - validation.validatePrimaryPhone(updateState.primaryPhone) - val validatedSecondaryPhone = - validation.validateSecondaryPhone(updateState.secondaryPhone) + val validatedPPhone = validation.validatePrimaryPhone(updateState.primaryPhone) + val validatedSPhone = validation.validateSecondaryPhone(updateState.secondaryPhone) val validatedAddress = validation.validateRestaurantAddress(updateState.address) - val validatedPaymentQrCode = - validation.validatePaymentQrCode(updateState.paymentQrCode) - val validatedDesc = - validation.validatePaymentQrCode(updateState.description) - + val validatedQrCode = validation.validatePaymentQrCode(updateState.paymentQrCode) + val validatedDesc = validation.validatePaymentQrCode(updateState.description) val hasError = listOf( validatedName, validatedTagLine, validatedEmail, - validatedPrimaryPhone, - validatedSecondaryPhone, + validatedPPhone, + validatedSPhone, validatedAddress, validatedDesc, - validatedPaymentQrCode + validatedQrCode ).any { !it.successful } if (hasError) { @@ -185,10 +180,10 @@ class ProfileViewModel @Inject constructor( nameError = validatedName.errorMessage, taglineError = validatedTagLine.errorMessage, emailError = validatedEmail.errorMessage, - primaryPhoneError = validatedPrimaryPhone.errorMessage, - secondaryPhoneError = validatedSecondaryPhone.errorMessage, + primaryPhoneError = validatedPPhone.errorMessage, + secondaryPhoneError = validatedSPhone.errorMessage, addressError = validatedAddress.errorMessage, - paymentQrCodeError = validatedPaymentQrCode.errorMessage, + paymentQrCodeError = validatedQrCode.errorMessage, descriptionError = validatedDesc.errorMessage ) diff --git a/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantCard.kt b/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantCard.kt index 5f51840d..f9d19254 100644 --- a/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantCard.kt +++ b/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantCard.kt @@ -2,36 +2,52 @@ package com.niyaj.feature.profile.components import android.annotation.SuppressLint import android.graphics.Bitmap +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.Crossfade import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ButtonDefaults import androidx.compose.material.Card +import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AddAPhoto +import androidx.compose.material.icons.filled.AddToPhotos +import androidx.compose.material.icons.filled.ImageSearch import androidx.compose.material.icons.rounded.Edit import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp -import com.niyaj.core.ui.R -import com.niyaj.designsystem.theme.ProfilePictureSizeLarge +import com.niyaj.designsystem.theme.LightColor6 +import com.niyaj.designsystem.theme.SpaceMini import com.niyaj.designsystem.theme.SpaceSmall import com.niyaj.model.RestaurantInfo +import com.niyaj.ui.components.NoteText +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedButton @SuppressLint("UnusedBoxWithConstraintsScope") @Composable @@ -39,7 +55,6 @@ fun RestaurantCard( modifier: Modifier = Modifier, info: RestaurantInfo, showPrintLogo: Boolean = false, - bannerRes: ImageVector = ImageVector.vectorResource(id = R.drawable.banner), printLogo: Bitmap? = null, resLogo: Bitmap? = null, onClickEdit: () -> Unit, @@ -53,9 +68,14 @@ fun RestaurantCard( modifier = modifier .fillMaxWidth() .background(MaterialTheme.colors.onPrimary), - shape = RoundedCornerShape(SpaceSmall), + shape = RoundedCornerShape( + topStart = 0.dp, + topEnd = 0.dp, + bottomStart = SpaceSmall, + bottomEnd = SpaceSmall + ), backgroundColor = MaterialTheme.colors.onPrimary, - elevation = 1.dp, + elevation = 0.dp, ) { Column( modifier = Modifier @@ -66,20 +86,18 @@ fun RestaurantCard( modifier = Modifier .fillMaxWidth(), ) { - Image( - imageVector = bannerRes, - contentDescription = "Banner Image", - contentScale = ContentScale.Crop, + Box( modifier = Modifier .fillMaxWidth() - .height(150.dp) + .height(80.dp) + .background(MaterialTheme.colors.primary) ) Box( modifier = Modifier .align(Alignment.BottomCenter) .offset( - y = ProfilePictureSizeLarge / 2 + y = 30.dp ) ) { ProfileImage( @@ -106,7 +124,8 @@ fun RestaurantCard( } RestaurantDetails( - modifier = Modifier.padding(top = (ProfilePictureSizeLarge / 2)), + modifier = Modifier + .padding(top = 40.dp), info = info, printLogo = printLogo, showPrintLogo = showPrintLogo, @@ -115,4 +134,181 @@ fun RestaurantCard( ) } } +} + +@SuppressLint("UnusedBoxWithConstraintsScope") +@Composable +fun UpdatedRestaurantCard( + modifier: Modifier = Modifier, + info: RestaurantInfo, + showPrintLogo: Boolean = false, + printLogo: Bitmap? = null, + resLogo: Bitmap? = null, + onClickEdit: () -> Unit, + onClickChangePrintLogo: () -> Unit, + onClickViewPrintLogo: () -> Unit, +) { + val iconSize = 24.dp + val offsetInPx = LocalDensity.current.run { (iconSize / 2).roundToPx() } + + Card( + modifier = modifier + .fillMaxWidth() + .background(MaterialTheme.colors.onPrimary), + shape = RoundedCornerShape( + topStart = 0.dp, + topEnd = 0.dp, + bottomStart = SpaceSmall, + bottomEnd = SpaceSmall + ), + backgroundColor = MaterialTheme.colors.onPrimary, + elevation = 0.dp, + ) { + Column( + modifier = Modifier + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth(), + ) { + Box( + modifier = Modifier + .fillMaxWidth() + .height(80.dp) + .background(MaterialTheme.colors.primary) + ) + + Box( + modifier = Modifier + .align(Alignment.BottomCenter) + .offset( + y = 30.dp + ) + ) { + ProfileImage( + modifier = Modifier, + resLogo = resLogo + ) + + IconButton( + onClick = onClickEdit, + modifier = Modifier + .offset { + IntOffset(x = +offsetInPx, y = -offsetInPx) + } + .size(iconSize) + .align(Alignment.TopEnd) + ) { + Icon( + imageVector = Icons.Rounded.Edit, + contentDescription = "Change Image", + tint = MaterialTheme.colors.background + ) + } + } + } + + Spacer(modifier = Modifier.height(40.dp)) + + Text( + text = info.name, + style = MaterialTheme.typography.h5, + fontWeight = FontWeight.Bold, + ) + + Spacer(modifier = Modifier.height(SpaceMini)) + + Text( + text = info.tagline, + style = MaterialTheme.typography.body1, + fontWeight = FontWeight.Medium, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + + Crossfade( + targetState = info.printLogo.isEmpty(), + label = "isPrintLogoEmpty" + ) { + if (it) { + Divider(modifier = Modifier.fillMaxWidth()) + Spacer(modifier = Modifier.height(SpaceSmall)) + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + NoteText( + text = "You have not set your print logo, Click below to set.", + onClick = onClickChangePrintLogo + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + + StandardButton( + text = "Set Image", + icon = Icons.Default.AddAPhoto, + onClick = onClickChangePrintLogo, + colors = ButtonDefaults.buttonColors( + backgroundColor = MaterialTheme.colors.secondaryVariant + ) + ) + } + } else { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + StandardOutlinedButton( + text = "Change", + icon = Icons.Default.AddToPhotos, + onClick = onClickChangePrintLogo, + ) + + Spacer(modifier = Modifier.width(SpaceSmall)) + + StandardButton( + text = "View Image", + icon = Icons.Default.ImageSearch, + onClick = onClickViewPrintLogo + ) + } + } + } + + AnimatedVisibility( + visible = showPrintLogo && printLogo != null, + ) { + printLogo?.let { + Spacer(modifier = Modifier.height(SpaceSmall)) + + Card( + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .padding(SpaceSmall), + backgroundColor = LightColor6 + ) { + Image( + bitmap = it.asImageBitmap(), + contentDescription = "Print Logo", + contentScale = ContentScale.Inside, + modifier = Modifier + .fillMaxSize() + .padding(SpaceSmall) + .align(Alignment.CenterHorizontally) + ) + } + } + } + } + } } \ No newline at end of file From 45be49020b43cec5f5e11f200e08575576baa7aa Mon Sep 17 00:00:00 2001 From: Sk Niyaj Ali Date: Mon, 8 Apr 2024 23:24:42 +0530 Subject: [PATCH 2/2] Enhancement - UpdateProfileScreen UI - Close #211 --- .../RestaurantInfoRepositoryImpl.kt | 5 +- .../main/java/com/niyaj/ui/util/Screens.kt | 1 + .../profile/add_edit/UpdateProfileScreen.kt | 296 ++++++++++++++---- .../profile/components/RestaurantDetails.kt | 6 +- 4 files changed, 239 insertions(+), 69 deletions(-) diff --git a/core/data/src/main/java/com/niyaj/data/data/repository/RestaurantInfoRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/RestaurantInfoRepositoryImpl.kt index 1e9c2c87..8f3b8ddb 100644 --- a/core/data/src/main/java/com/niyaj/data/data/repository/RestaurantInfoRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/RestaurantInfoRepositoryImpl.kt @@ -20,6 +20,7 @@ import com.niyaj.common.tags.ProfileTestTags.TAG_EMPTY_ERROR import com.niyaj.common.utils.Resource import com.niyaj.common.utils.ValidationResult import com.niyaj.common.utils.isValidPassword +import com.niyaj.data.mapper.toEntity import com.niyaj.data.repository.RestaurantInfoRepository import com.niyaj.data.repository.validation.RestaurantInfoValidationRepository import com.niyaj.database.model.RestaurantInfoEntity @@ -87,7 +88,7 @@ class RestaurantInfoRepositoryImpl( restaurant.logo = imageName restaurant.updatedAt = System.currentTimeMillis().toString() } else { - val newRestaurant = RestaurantInfoEntity() + val newRestaurant = RestaurantInfo().toEntity() newRestaurant.restaurantId = RESTAURANT_ID newRestaurant.logo = imageName newRestaurant.createdAt = System.currentTimeMillis().toString() @@ -115,7 +116,7 @@ class RestaurantInfoRepositoryImpl( restaurant.printLogo = imageName restaurant.updatedAt = System.currentTimeMillis().toString() } else { - val newRestaurant = RestaurantInfoEntity() + val newRestaurant = RestaurantInfo().toEntity() newRestaurant.restaurantId = RESTAURANT_ID newRestaurant.printLogo = imageName newRestaurant.createdAt = System.currentTimeMillis().toString() diff --git a/core/ui/src/main/java/com/niyaj/ui/util/Screens.kt b/core/ui/src/main/java/com/niyaj/ui/util/Screens.kt index be0003cc..5869e885 100644 --- a/core/ui/src/main/java/com/niyaj/ui/util/Screens.kt +++ b/core/ui/src/main/java/com/niyaj/ui/util/Screens.kt @@ -12,6 +12,7 @@ object Screens { const val HOME_SCREEN = "home_screen" const val PROFILE_SCREEN = "profile_screen" + const val UPDATE_PROFILE_SCREEN = "update_profile_screen" const val CART_SCREEN = "cart_screen" diff --git a/feature/profile/src/main/java/com/niyaj/feature/profile/add_edit/UpdateProfileScreen.kt b/feature/profile/src/main/java/com/niyaj/feature/profile/add_edit/UpdateProfileScreen.kt index 7a7e1fff..4891e4ea 100644 --- a/feature/profile/src/main/java/com/niyaj/feature/profile/add_edit/UpdateProfileScreen.kt +++ b/feature/profile/src/main/java/com/niyaj/feature/profile/add_edit/UpdateProfileScreen.kt @@ -1,15 +1,27 @@ package com.niyaj.feature.profile.add_edit +import android.Manifest +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Spacer 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.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Icon import androidx.compose.material.IconButton +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Notes import androidx.compose.material.icons.automirrored.filled.StarHalf import androidx.compose.material.icons.filled.Edit @@ -20,16 +32,26 @@ import androidx.compose.material.icons.filled.PhoneAndroid import androidx.compose.material.icons.filled.QrCode import androidx.compose.material.icons.filled.QrCodeScanner import androidx.compose.material.icons.filled.Restaurant +import androidx.compose.material.rememberScaffoldState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +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.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.rememberMultiplePermissionsState import com.niyaj.common.tags.ProfileTestTags.ADDRESS_ERROR_FIELD import com.niyaj.common.tags.ProfileTestTags.ADDRESS_FIELD import com.niyaj.common.tags.ProfileTestTags.ADD_EDIT_PROFILE_BTN @@ -48,28 +70,111 @@ import com.niyaj.common.tags.ProfileTestTags.S_PHONE_FIELD import com.niyaj.common.tags.ProfileTestTags.TAG_ERROR_FIELD import com.niyaj.common.tags.ProfileTestTags.TAG_FIELD import com.niyaj.common.tags.ProfileTestTags.UPDATE_PROFILE +import com.niyaj.common.utils.ImageStorageManager +import com.niyaj.common.utils.toBitmap import com.niyaj.designsystem.theme.SpaceSmall import com.niyaj.feature.profile.ProfileEvent import com.niyaj.feature.profile.ProfileViewModel +import com.niyaj.feature.profile.components.UpdatedRestaurantCard +import com.niyaj.model.RESTAURANT_LOGO_NAME +import com.niyaj.model.RESTAURANT_PRINT_LOGO_NAME import com.niyaj.ui.components.StandardButtonFW import com.niyaj.ui.components.StandardOutlinedTextField import com.niyaj.ui.event.UiEvent -import com.niyaj.ui.util.BottomSheetWithCloseDialog +import com.niyaj.ui.util.Screens import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.ResultBackNavigator -import com.ramcosta.composedestinations.spec.DestinationStyleBottomSheet +import kotlinx.coroutines.launch -@Destination(style = DestinationStyleBottomSheet::class) +@OptIn(ExperimentalPermissionsApi::class) +@Destination(route = Screens.UPDATE_PROFILE_SCREEN) @Composable fun UpdateProfileScreen( navController: NavController = rememberNavController(), - profileViewModel: ProfileViewModel = hiltViewModel(), + viewModel: ProfileViewModel = hiltViewModel(), resultBackNavigator: ResultBackNavigator ) { - val scannedBitmap = profileViewModel.scannedBitmap.collectAsStateWithLifecycle().value + val scaffoldState = rememberScaffoldState() + val context = LocalContext.current + val lazyListState = rememberLazyListState() + val scope = rememberCoroutineScope() + + val info = viewModel.info.collectAsStateWithLifecycle().value + + val resLogo = info.getRestaurantLogo(context) + val printLogo = info.getRestaurantPrintLogo(context) + + val scannedBitmap = viewModel.scannedBitmap.collectAsStateWithLifecycle().value + + val permissionState = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + rememberMultiplePermissionsState( + permissions = listOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.READ_MEDIA_IMAGES + ) + ) + } else { + rememberMultiplePermissionsState( + permissions = listOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + ) + ) + } + + fun checkForMediaPermission() { + if (!permissionState.allPermissionsGranted) { + permissionState.launchMultiplePermissionRequest() + } + } + + var showPrintLogo by rememberSaveable { + mutableStateOf(false) + } + + val resLogoLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickVisualMedia() + ) { uri -> + uri?.let { + val result = ImageStorageManager.saveToInternalStorage( + context, + uri.toBitmap(context), + RESTAURANT_LOGO_NAME + ) + + scope.launch { + if (result) { + scaffoldState.snackbarHostState.showSnackbar("Profile image saved successfully.") + viewModel.onEvent(ProfileEvent.LogoChanged) + } else { + scaffoldState.snackbarHostState.showSnackbar("Unable save image into storage.") + } + } + } + } + + val printLogoLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.PickVisualMedia() + ) { uri -> + uri?.let { + val result = ImageStorageManager.saveToInternalStorage( + context, + uri.toBitmap(context), + RESTAURANT_PRINT_LOGO_NAME + ) + + scope.launch { + if (result) { + scaffoldState.snackbarHostState.showSnackbar("Print Image saved successfully.") + viewModel.onEvent(ProfileEvent.PrintLogoChanged) + } else { + scaffoldState.snackbarHostState.showSnackbar("Unable save print image into storage.") + } + } + } + } LaunchedEffect(key1 = true) { - profileViewModel.eventFlow.collect { event -> + viewModel.eventFlow.collect { event -> when (event) { is UiEvent.Success -> { resultBackNavigator.navigateBack(event.successMessage) @@ -83,145 +188,207 @@ fun UpdateProfileScreen( } LaunchedEffect(key1 = true) { - profileViewModel.onEvent(ProfileEvent.SetProfileInfo) + viewModel.onEvent(ProfileEvent.SetProfileInfo) + } + + LaunchedEffect(key1 = true) { + viewModel.eventFlow.collect { event -> + when (event) { + is UiEvent.Success -> { + scaffoldState.snackbarHostState.showSnackbar( + message = event.successMessage + ) + } + + is UiEvent.Error -> { + scaffoldState.snackbarHostState.showSnackbar( + message = event.errorMessage + ) + } + + } + } } - BottomSheetWithCloseDialog( - modifier = Modifier.fillMaxSize(), - text = UPDATE_PROFILE, - onClosePressed = { - navController.navigateUp() + Scaffold( + scaffoldState = scaffoldState, + modifier = Modifier + .fillMaxSize(), + topBar = { + TopAppBar( + title = { Text(text = UPDATE_PROFILE) }, + navigationIcon = { + IconButton( + onClick = { navController.navigateUp() } + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Navigate Back" + ) + } + }, + elevation = 0.dp + ) } - ) { + ) { paddingValues -> LazyColumn( modifier = Modifier .fillMaxWidth() + .padding(paddingValues), + state = lazyListState, + verticalArrangement = Arrangement.spacedBy(SpaceSmall) ) { + item("UpdatedRestaurantCard") { + UpdatedRestaurantCard( + info = info, + resLogo = resLogo, + printLogo = printLogo, + showPrintLogo = showPrintLogo, + onClickEdit = { + checkForMediaPermission() + resLogoLauncher.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) + ) + }, + onClickChangePrintLogo = { + checkForMediaPermission() + printLogoLauncher.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly) + ) + }, + onClickViewPrintLogo = { + showPrintLogo = !showPrintLogo + } + ) + } + item(NAME_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(NAME_FIELD), - text = profileViewModel.updateState.name, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(NAME_FIELD), + text = viewModel.updateState.name, label = NAME_FIELD, leadingIcon = Icons.Default.Restaurant, - error = profileViewModel.updateState.nameError, + error = viewModel.updateState.nameError, errorTag = NAME_ERROR_FIELD, onValueChange = { - profileViewModel.onEvent(ProfileEvent.NameChanged(it)) + viewModel.onEvent(ProfileEvent.NameChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMAIL_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(EMAIL_FIELD), - text = profileViewModel.updateState.email, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(EMAIL_FIELD), + text = viewModel.updateState.email, label = EMAIL_FIELD, leadingIcon = Icons.Default.Email, errorTag = EMAIL_ERROR_FIELD, - error = profileViewModel.updateState.emailError, + error = viewModel.updateState.emailError, onValueChange = { - profileViewModel.onEvent(ProfileEvent.NameChanged(it)) + viewModel.onEvent(ProfileEvent.NameChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(P_PHONE_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(P_PHONE_FIELD), - text = profileViewModel.updateState.primaryPhone, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(P_PHONE_FIELD), + text = viewModel.updateState.primaryPhone, label = P_PHONE_FIELD, leadingIcon = Icons.Default.PhoneAndroid, errorTag = P_PHONE_ERROR_FIELD, - error = profileViewModel.updateState.primaryPhoneError, + error = viewModel.updateState.primaryPhoneError, onValueChange = { - profileViewModel.onEvent(ProfileEvent.PrimaryPhoneChanged(it)) + viewModel.onEvent(ProfileEvent.PrimaryPhoneChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(S_PHONE_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(S_PHONE_FIELD), - text = profileViewModel.updateState.secondaryPhone, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(S_PHONE_FIELD), + text = viewModel.updateState.secondaryPhone, label = S_PHONE_FIELD, leadingIcon = Icons.Default.Phone, errorTag = S_PHONE_ERROR_FIELD, - error = profileViewModel.updateState.secondaryPhoneError, + error = viewModel.updateState.secondaryPhoneError, onValueChange = { - profileViewModel.onEvent(ProfileEvent.SecondaryPhoneChanged(it)) + viewModel.onEvent(ProfileEvent.SecondaryPhoneChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(TAG_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(TAG_FIELD), - text = profileViewModel.updateState.tagline, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(TAG_FIELD), + text = viewModel.updateState.tagline, label = TAG_FIELD, errorTag = TAG_ERROR_FIELD, leadingIcon = Icons.AutoMirrored.Filled.StarHalf, - error = profileViewModel.updateState.taglineError, + error = viewModel.updateState.taglineError, onValueChange = { - profileViewModel.onEvent(ProfileEvent.TaglineChanged(it)) + viewModel.onEvent(ProfileEvent.TaglineChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(ADDRESS_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(ADDRESS_FIELD), - text = profileViewModel.updateState.address, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(ADDRESS_FIELD), + text = viewModel.updateState.address, label = ADDRESS_FIELD, maxLines = 2, leadingIcon = Icons.Default.LocationOn, errorTag = ADDRESS_ERROR_FIELD, - error = profileViewModel.updateState.addressError, + error = viewModel.updateState.addressError, onValueChange = { - profileViewModel.onEvent(ProfileEvent.AddressChanged(it)) + viewModel.onEvent(ProfileEvent.AddressChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(DESC_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(DESC_FIELD), - text = profileViewModel.updateState.description, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(DESC_FIELD), + text = viewModel.updateState.description, label = DESC_FIELD, singleLine = false, maxLines = 2, leadingIcon = Icons.AutoMirrored.Filled.Notes, - error = profileViewModel.updateState.descriptionError, + error = viewModel.updateState.descriptionError, errorTag = DESC_ERROR_FIELD, onValueChange = { - profileViewModel.onEvent(ProfileEvent.DescriptionChanged(it)) + viewModel.onEvent(ProfileEvent.DescriptionChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(QR_CODE_FIELD) { StandardOutlinedTextField( - modifier = Modifier.testTag(QR_CODE_FIELD), - text = profileViewModel.updateState.paymentQrCode, + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(QR_CODE_FIELD), + text = viewModel.updateState.paymentQrCode, label = QR_CODE_FIELD, leadingIcon = Icons.Default.QrCode, trailingIcon = { IconButton( onClick = { - profileViewModel.onEvent(ProfileEvent.StartScanning) + viewModel.onEvent(ProfileEvent.StartScanning) } ) { Icon( @@ -230,21 +397,18 @@ fun UpdateProfileScreen( ) } }, - error = profileViewModel.updateState.paymentQrCodeError, + error = viewModel.updateState.paymentQrCodeError, errorTag = QR_CODE_ERROR, singleLine = false, maxLines = 4, onValueChange = { - profileViewModel.onEvent(ProfileEvent.PaymentQrCodeChanged(it)) + viewModel.onEvent(ProfileEvent.PaymentQrCodeChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item("Scanned Bitmap") { if (scannedBitmap != null) { - Spacer(modifier = Modifier.height(SpaceSmall)) Box( modifier = Modifier @@ -268,11 +432,13 @@ fun UpdateProfileScreen( Spacer(modifier = Modifier.height(SpaceSmall)) StandardButtonFW( - modifier = Modifier.testTag(ADD_EDIT_PROFILE_BTN), + modifier = Modifier + .padding(horizontal = SpaceSmall) + .testTag(ADD_EDIT_PROFILE_BTN), text = UPDATE_PROFILE, icon = Icons.Default.Edit, onClick = { - profileViewModel.onEvent(ProfileEvent.UpdateProfile) + viewModel.onEvent(ProfileEvent.UpdateProfile) } ) diff --git a/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantDetails.kt b/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantDetails.kt index 36992165..ea1b1a86 100644 --- a/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantDetails.kt +++ b/feature/profile/src/main/java/com/niyaj/feature/profile/components/RestaurantDetails.kt @@ -47,7 +47,9 @@ fun RestaurantDetails( onClickViewPrintLogo : () -> Unit, ) { Column( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .padding(SpaceSmall), horizontalAlignment = Alignment.CenterHorizontally, ) { Text( @@ -157,7 +159,7 @@ fun RestaurantDetails( Spacer(modifier = Modifier.width(SpaceSmall)) StandardButton( - text = "View Image", + text = if (!showPrintLogo) "View Image" else "Hide Image", icon = Icons.Default.ImageSearch, onClick = onClickViewPrintLogo )