feat: change drawer
This commit is contained in:
@@ -21,8 +21,10 @@ import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -33,6 +35,7 @@ import com.approagency.pharmacy.domain.repository.AuthRepository
|
||||
import com.approagency.pharmacy.presentation.account.AccountDrawer
|
||||
import com.approagency.pharmacy.presentation.account.AccountSheet
|
||||
import com.approagency.pharmacy.presentation.account.AccountSheetController
|
||||
import com.approagency.pharmacy.presentation.common.ConfirmBottomSheet
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@@ -52,6 +55,7 @@ fun MainContainer(
|
||||
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
var showLogoutConfirm by remember { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
val appVersion = remember {
|
||||
@@ -68,12 +72,7 @@ fun MainContainer(
|
||||
appVersion = appVersion,
|
||||
themeMode = themeMode,
|
||||
onThemeModeChange = { session.setThemeMode(it) },
|
||||
onLogout = {
|
||||
scope.launch {
|
||||
authRepository.logout()
|
||||
drawerState.close()
|
||||
}
|
||||
}
|
||||
onLogout = { showLogoutConfirm = true }
|
||||
)
|
||||
}
|
||||
) {
|
||||
@@ -96,6 +95,22 @@ fun MainContainer(
|
||||
if (sheetVisible) {
|
||||
AccountSheet(onDismiss = { sheetController.hide() })
|
||||
}
|
||||
|
||||
if (showLogoutConfirm) {
|
||||
ConfirmBottomSheet(
|
||||
title = "خروج از حساب",
|
||||
message = "آیا میخواهید از حساب کاربری خود خارج شوید؟",
|
||||
confirmText = "خروج",
|
||||
onConfirm = {
|
||||
showLogoutConfirm = false
|
||||
scope.launch {
|
||||
authRepository.logout()
|
||||
drawerState.close()
|
||||
}
|
||||
},
|
||||
onDismiss = { showLogoutConfirm = false }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
@@ -61,7 +62,10 @@ fun AccountDrawer(
|
||||
onThemeModeChange: (ThemeMode) -> Unit,
|
||||
onLogout: () -> Unit
|
||||
) {
|
||||
ModalDrawerSheet(drawerContainerColor = MaterialTheme.colorScheme.surface) {
|
||||
ModalDrawerSheet(
|
||||
modifier = Modifier.width(280.dp),
|
||||
drawerContainerColor = MaterialTheme.colorScheme.surface
|
||||
) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -37,9 +38,9 @@ import androidx.compose.ui.unit.dp
|
||||
import com.approagency.pharmacy.MainActivity
|
||||
import com.approagency.pharmacy.domain.model.SubscriptionProduct
|
||||
import com.approagency.pharmacy.presentation.common.CustomModalBottomSheet
|
||||
import com.approagency.pharmacy.presentation.common.Loading
|
||||
import com.approagency.pharmacy.presentation.common.OtpTextField
|
||||
import com.approagency.pharmacy.presentation.common.PrimaryButton
|
||||
import com.approagency.pharmacy.presentation.common.shimmer
|
||||
import com.approagency.pharmacy.presentation.viewModel.AccountPhase
|
||||
import com.approagency.pharmacy.presentation.viewModel.AccountViewModel
|
||||
import com.vada.caller.ui.theme.dime
|
||||
@@ -147,11 +148,11 @@ fun AccountSheet(
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
when {
|
||||
ui.productsLoading -> Loading(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(160.dp)
|
||||
)
|
||||
ui.productsLoading -> Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
||||
) {
|
||||
repeat(2) { ProductCardSkeleton() }
|
||||
}
|
||||
else -> Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
||||
) {
|
||||
@@ -182,8 +183,6 @@ fun AccountSheet(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
TextButton(onClick = { viewModel.logout() }) { Text("خروج از حساب") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,6 +240,39 @@ private fun ProductCard(
|
||||
}
|
||||
}
|
||||
|
||||
/** جایگیرندهی shimmer برای کارت محصول هنگام بارگذاری. */
|
||||
@Composable
|
||||
private fun ProductCardSkeleton() {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = Modifier.padding(MaterialTheme.dime.lg)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.width(120.dp)
|
||||
.height(18.dp)
|
||||
.shimmer()
|
||||
)
|
||||
Box(
|
||||
Modifier
|
||||
.width(72.dp)
|
||||
.height(18.dp)
|
||||
.shimmer()
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(MaterialTheme.dime.md))
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp)
|
||||
.shimmer(shape = MaterialTheme.shapes.medium)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** قالببندی قیمت با جداکنندهی هزارگان. */
|
||||
private fun formatPrice(price: Long): String =
|
||||
price.toString().reversed().chunked(3).joinToString(",").reversed()
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.approagency.pharmacy.presentation.common
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vada.caller.ui.theme.dime
|
||||
|
||||
/**
|
||||
* شیتِ تأییدِ عمومی برای کنشهای حساس (مثل خروج از حساب).
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ConfirmBottomSheet(
|
||||
title: String,
|
||||
message: String,
|
||||
confirmText: String,
|
||||
onConfirm: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
dismissText: String = "انصراف",
|
||||
confirmColor: Color = MaterialTheme.colorScheme.error
|
||||
) {
|
||||
CustomModalBottomSheet(onDismiss = onDismiss) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(MaterialTheme.dime.lg)
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.sm))
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.xl))
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)) {
|
||||
OutlinedButton(
|
||||
onClick = onDismiss,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(48.dp)
|
||||
) { Text(dismissText) }
|
||||
Button(
|
||||
onClick = onConfirm,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(48.dp),
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = confirmColor,
|
||||
contentColor = MaterialTheme.colorScheme.onError
|
||||
)
|
||||
) { Text(confirmText) }
|
||||
}
|
||||
Spacer(Modifier.height(MaterialTheme.dime.sm))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.approagency.pharmacy.presentation.common
|
||||
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
||||
/**
|
||||
* افکت درخششِ متحرک (shimmer) برای جایگیرندههای لودینگ.
|
||||
* رنگها از تم گرفته میشوند تا در حالت تاریک/روشن هماهنگ باشد.
|
||||
*/
|
||||
fun Modifier.shimmer(shape: Shape = RoundedCornerShape(8.dp)): Modifier = composed {
|
||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||
val transition = rememberInfiniteTransition(label = "shimmer")
|
||||
val startX by transition.animateFloat(
|
||||
initialValue = -2f * size.width.coerceAtLeast(1),
|
||||
targetValue = 2f * size.width.coerceAtLeast(1),
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = 1200),
|
||||
repeatMode = RepeatMode.Restart
|
||||
),
|
||||
label = "shimmerX"
|
||||
)
|
||||
val base = MaterialTheme.colorScheme.surfaceVariant
|
||||
val highlight = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
|
||||
clip(shape)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(base, highlight, base),
|
||||
start = Offset(startX, 0f),
|
||||
end = Offset(startX + size.width.toFloat(), size.height.toFloat())
|
||||
)
|
||||
)
|
||||
.onGloballyPositioned { size = it.size }
|
||||
}
|
||||
Reference in New Issue
Block a user