feat: theme manage and auth
This commit is contained in:
@@ -5,6 +5,8 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.approagency.pharmacy.data.local.SessionManager
|
||||
import com.approagency.pharmacy.domain.repository.AuthRepository
|
||||
@@ -23,7 +25,8 @@ class MainActivity : ComponentActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
DrugTheme {
|
||||
val themeMode by session.themeMode.collectAsState()
|
||||
DrugTheme(themeMode = themeMode) {
|
||||
AppNavGraph()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,21 @@ data class ProductDto(
|
||||
@SerializedName("price") val price: Long? = null,
|
||||
@SerializedName("type") val type: Int? = null,
|
||||
@SerializedName("uuid") val uuid: String? = null,
|
||||
@SerializedName("descriptions") val descriptions: String? = null
|
||||
@SerializedName("descriptions") val descriptions: String? = null,
|
||||
@SerializedName("expires_at") val expiresAt: String? = null,
|
||||
@SerializedName("expire_at") val expireAt: String? = null,
|
||||
@SerializedName("pivot") val pivot: ProductPivot? = null
|
||||
) {
|
||||
/** تاریخ انقضای اشتراکِ این کاربر؛ از pivot یا فیلدهای مستقیمِ محصول. */
|
||||
val resolvedExpireAt: String?
|
||||
get() = pivot?.expiresAt ?: pivot?.expireAt ?: expiresAt ?: expireAt
|
||||
}
|
||||
|
||||
/** اطلاعات رابطهی کاربر-محصول (شامل تاریخ انقضای خرید). */
|
||||
data class ProductPivot(
|
||||
@SerializedName("expires_at") val expiresAt: String? = null,
|
||||
@SerializedName("expire_at") val expireAt: String? = null,
|
||||
@SerializedName("created_at") val createdAt: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -55,6 +69,9 @@ data class StatusDto(
|
||||
/** عنوان اشتراک جاری برای نمایش در نوار بالای اپ. */
|
||||
val subscriptionTitle: String? get() = products?.firstOrNull()?.title
|
||||
|
||||
/** تاریخ انقضای اشتراک جاری. */
|
||||
val subscriptionExpireAt: String? get() = products?.firstOrNull()?.resolvedExpireAt
|
||||
|
||||
val displayName: String? get() = fullName?.takeIf { it.isNotBlank() }
|
||||
?: listOfNotNull(firstName, lastName).joinToString(" ").takeIf { it.isNotBlank() }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.approagency.pharmacy.data.local
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.approagency.pharmacy.ui.theme.ThemeMode
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -12,7 +13,8 @@ data class AccountState(
|
||||
val mobile: String? = null,
|
||||
val displayName: String? = null,
|
||||
val isSubscribed: Boolean = false,
|
||||
val subscriptionTitle: String? = null
|
||||
val subscriptionTitle: String? = null,
|
||||
val subscriptionExpireAt: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
@@ -75,13 +77,15 @@ class SessionManager(context: Context) {
|
||||
mobile: String?,
|
||||
displayName: String?,
|
||||
isSubscribed: Boolean,
|
||||
subscriptionTitle: String?
|
||||
subscriptionTitle: String?,
|
||||
subscriptionExpireAt: String?
|
||||
) {
|
||||
prefs.edit()
|
||||
.putString(KEY_MOBILE, mobile)
|
||||
.putString(KEY_DISPLAY_NAME, displayName)
|
||||
.putBoolean(KEY_IS_SUBSCRIBED, isSubscribed)
|
||||
.putString(KEY_SUB_TITLE, subscriptionTitle)
|
||||
.putString(KEY_SUB_EXPIRE, subscriptionExpireAt)
|
||||
.apply()
|
||||
publishAccount()
|
||||
}
|
||||
@@ -101,6 +105,7 @@ class SessionManager(context: Context) {
|
||||
.remove(KEY_DISPLAY_NAME)
|
||||
.remove(KEY_IS_SUBSCRIBED)
|
||||
.remove(KEY_SUB_TITLE)
|
||||
.remove(KEY_SUB_EXPIRE)
|
||||
.apply()
|
||||
publishAccount()
|
||||
}
|
||||
@@ -109,12 +114,27 @@ class SessionManager(context: Context) {
|
||||
_account.value = readAccount()
|
||||
}
|
||||
|
||||
// ---------- حالت نمایش (تم) ----------
|
||||
|
||||
private val _themeMode = MutableStateFlow(readThemeMode())
|
||||
val themeMode: StateFlow<ThemeMode> = _themeMode.asStateFlow()
|
||||
|
||||
fun setThemeMode(mode: ThemeMode) {
|
||||
prefs.edit().putString(KEY_THEME_MODE, mode.name).apply()
|
||||
_themeMode.value = mode
|
||||
}
|
||||
|
||||
private fun readThemeMode(): ThemeMode =
|
||||
runCatching { ThemeMode.valueOf(prefs.getString(KEY_THEME_MODE, null) ?: ThemeMode.SYSTEM.name) }
|
||||
.getOrDefault(ThemeMode.SYSTEM)
|
||||
|
||||
private fun readAccount() = AccountState(
|
||||
isLoggedIn = !token.isNullOrBlank(),
|
||||
mobile = prefs.getString(KEY_MOBILE, null),
|
||||
displayName = prefs.getString(KEY_DISPLAY_NAME, null),
|
||||
isSubscribed = prefs.getBoolean(KEY_IS_SUBSCRIBED, false),
|
||||
subscriptionTitle = prefs.getString(KEY_SUB_TITLE, null)
|
||||
subscriptionTitle = prefs.getString(KEY_SUB_TITLE, null),
|
||||
subscriptionExpireAt = prefs.getString(KEY_SUB_EXPIRE, null)
|
||||
)
|
||||
|
||||
private companion object {
|
||||
@@ -125,5 +145,7 @@ class SessionManager(context: Context) {
|
||||
const val KEY_FREE_SEARCH_COUNT = "free_search_count"
|
||||
const val KEY_IS_SUBSCRIBED = "is_subscribed"
|
||||
const val KEY_SUB_TITLE = "subscription_title"
|
||||
const val KEY_SUB_EXPIRE = "subscription_expire_at"
|
||||
const val KEY_THEME_MODE = "theme_mode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ class AuthRepositoryImpl(
|
||||
mobile = dto.mobile ?: session.account.value.mobile,
|
||||
displayName = dto.displayName,
|
||||
isSubscribed = dto.isSubscribed,
|
||||
subscriptionTitle = dto.subscriptionTitle
|
||||
subscriptionTitle = dto.subscriptionTitle,
|
||||
subscriptionExpireAt = dto.subscriptionExpireAt
|
||||
)
|
||||
return dto.isSubscribed
|
||||
}
|
||||
|
||||
@@ -5,24 +5,35 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.DrawerValue
|
||||
import androidx.compose.material3.ElevatedButton
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalNavigationDrawer
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.rememberDrawerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.navigation.NavHostController
|
||||
import com.approagency.pharmacy.data.local.AccountState
|
||||
import com.approagency.pharmacy.data.local.SessionManager
|
||||
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 kotlinx.coroutines.launch
|
||||
import org.koin.compose.koinInject
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -33,21 +44,52 @@ fun MainContainer(
|
||||
) {
|
||||
val session: SessionManager = koinInject()
|
||||
val sheetController: AccountSheetController = koinInject()
|
||||
val authRepository: AuthRepository = koinInject()
|
||||
|
||||
val account by session.account.collectAsState()
|
||||
val sheetVisible by sheetController.visible.collectAsState()
|
||||
val themeMode by session.themeMode.collectAsState()
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AccountAppBar(
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val context = LocalContext.current
|
||||
val appVersion = remember {
|
||||
runCatching {
|
||||
context.packageManager.getPackageInfo(context.packageName, 0).versionName
|
||||
}.getOrNull().orEmpty()
|
||||
}
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
AccountDrawer(
|
||||
account = account,
|
||||
onAccountClick = { sheetController.show() }
|
||||
appVersion = appVersion,
|
||||
themeMode = themeMode,
|
||||
onThemeModeChange = { session.setThemeMode(it) },
|
||||
onLogout = {
|
||||
scope.launch {
|
||||
authRepository.logout()
|
||||
drawerState.close()
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
bottomBar = { BottomBar(navController) }
|
||||
) { padding ->
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
content()
|
||||
}
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AccountAppBar(
|
||||
account = account,
|
||||
onMenuClick = { scope.launch { drawerState.open() } },
|
||||
onAccountClick = { sheetController.show() }
|
||||
)
|
||||
},
|
||||
bottomBar = { BottomBar(navController) }
|
||||
) { padding ->
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +102,18 @@ fun MainContainer(
|
||||
@Composable
|
||||
private fun AccountAppBar(
|
||||
account: AccountState,
|
||||
onMenuClick: () -> Unit,
|
||||
onAccountClick: () -> Unit
|
||||
) {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onMenuClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Menu,
|
||||
contentDescription = "منو"
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
if (account.isLoggedIn) {
|
||||
Column {
|
||||
@@ -84,15 +135,7 @@ private fun AccountAppBar(
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if (account.isLoggedIn) {
|
||||
IconButton(onClick = onAccountClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.AccountCircle,
|
||||
contentDescription = "حساب کاربری",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (!account.isLoggedIn) {
|
||||
TextButton(onClick = onAccountClick) {
|
||||
Text("ورود", textAlign = TextAlign.Center)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
package com.approagency.pharmacy.presentation.account
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Logout
|
||||
import androidx.compose.material.icons.filled.BrightnessAuto
|
||||
import androidx.compose.material.icons.filled.CalendarMonth
|
||||
import androidx.compose.material.icons.filled.DarkMode
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.LightMode
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Phone
|
||||
import androidx.compose.material.icons.filled.WorkspacePremium
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalDrawerSheet
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
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.approagency.pharmacy.data.local.AccountState
|
||||
import com.approagency.pharmacy.ui.theme.ThemeMode
|
||||
import com.vada.caller.ui.theme.dime
|
||||
|
||||
/**
|
||||
* کشوی کناری حساب کاربری با طراحی بهبودیافته:
|
||||
* - سربرگ با آواتار، نام/موبایل و نشانِ وضعیت اشتراک
|
||||
* - کارت اطلاعات (موبایل، اشتراک، تاریخ انقضا، نسخهی برنامه)
|
||||
* - انتخاب حالت نمایش (سیستم/روشن/تاریک)
|
||||
* - دکمهی خروج
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountDrawer(
|
||||
account: AccountState,
|
||||
appVersion: String,
|
||||
themeMode: ThemeMode,
|
||||
onThemeModeChange: (ThemeMode) -> Unit,
|
||||
onLogout: () -> Unit
|
||||
) {
|
||||
ModalDrawerSheet(drawerContainerColor = MaterialTheme.colorScheme.surface) {
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = MaterialTheme.dime.lg, vertical = MaterialTheme.dime.xl)
|
||||
) {
|
||||
Header(account)
|
||||
|
||||
Spacer(Modifier.height(MaterialTheme.dime.xl))
|
||||
|
||||
// کارت اطلاعات حساب
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceContainer,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(modifier = Modifier.padding(MaterialTheme.dime.md)) {
|
||||
InfoRow(
|
||||
icon = Icons.Filled.Phone,
|
||||
label = "شماره موبایل",
|
||||
value = account.mobile ?: "-"
|
||||
)
|
||||
InfoRow(
|
||||
icon = Icons.Filled.WorkspacePremium,
|
||||
label = "اشتراک",
|
||||
value = account.subscriptionTitle ?: "بدون اشتراک",
|
||||
highlight = account.isSubscribed
|
||||
)
|
||||
if (account.isSubscribed) {
|
||||
InfoRow(
|
||||
icon = Icons.Filled.CalendarMonth,
|
||||
label = "تاریخ انقضا",
|
||||
value = account.subscriptionExpireAt ?: "-"
|
||||
)
|
||||
}
|
||||
InfoRow(
|
||||
icon = Icons.Filled.Info,
|
||||
label = "نسخه برنامه",
|
||||
value = appVersion.ifBlank { "-" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(MaterialTheme.dime.xl))
|
||||
|
||||
// انتخاب حالت نمایش
|
||||
Text(
|
||||
text = "حالت نمایش",
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.sm))
|
||||
ThemeModeSelector(selected = themeMode, onSelect = onThemeModeChange)
|
||||
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
if (account.isLoggedIn) {
|
||||
OutlinedButton(
|
||||
onClick = onLogout,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Logout,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Spacer(Modifier.size(MaterialTheme.dime.sm))
|
||||
Text(
|
||||
text = "خروج از حساب",
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(account: AccountState) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.background(MaterialTheme.colorScheme.primaryContainer, CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Person,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.size(MaterialTheme.dime.md))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = account.displayName?.takeIf { it.isNotBlank() }
|
||||
?: account.mobile
|
||||
?: "کاربر مهمان",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.xs))
|
||||
SubscriptionBadge(isSubscribed = account.isSubscribed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SubscriptionBadge(isSubscribed: Boolean) {
|
||||
val container = if (isSubscribed)
|
||||
MaterialTheme.colorScheme.primaryContainer
|
||||
else
|
||||
MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
val content = if (isSubscribed)
|
||||
MaterialTheme.colorScheme.onPrimaryContainer
|
||||
else
|
||||
MaterialTheme.colorScheme.onSurfaceVariant
|
||||
|
||||
Surface(color = container, shape = CircleShape) {
|
||||
Text(
|
||||
text = if (isSubscribed) "اشتراک فعال" else "بدون اشتراک",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = content,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = MaterialTheme.dime.sm,
|
||||
vertical = MaterialTheme.dime.xxs
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoRow(
|
||||
icon: ImageVector,
|
||||
label: String,
|
||||
value: String,
|
||||
highlight: Boolean = false
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = MaterialTheme.dime.sm),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(Modifier.size(MaterialTheme.dime.sm))
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Text(
|
||||
text = value,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = if (highlight)
|
||||
MaterialTheme.colorScheme.primary
|
||||
else
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ThemeModeSelector(
|
||||
selected: ThemeMode,
|
||||
onSelect: (ThemeMode) -> Unit
|
||||
) {
|
||||
val options = listOf(
|
||||
Triple(ThemeMode.SYSTEM, "سیستم", Icons.Filled.BrightnessAuto),
|
||||
Triple(ThemeMode.LIGHT, "روشن", Icons.Filled.LightMode),
|
||||
Triple(ThemeMode.DARK, "تاریک", Icons.Filled.DarkMode)
|
||||
)
|
||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
||||
options.forEachIndexed { index, (mode, label, icon) ->
|
||||
SegmentedButton(
|
||||
selected = selected == mode,
|
||||
onClick = { onSelect(mode) },
|
||||
shape = SegmentedButtonDefaults.itemShape(index, options.size),
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
},
|
||||
label = { Text(label, style = MaterialTheme.typography.labelMedium) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.approagency.pharmacy.presentation.account
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -29,6 +30,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.approagency.pharmacy.domain.model.SubscriptionProduct
|
||||
@@ -77,17 +79,20 @@ fun AccountSheet(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
OutlinedTextField(
|
||||
value = viewModel.mobile,
|
||||
onValueChange = viewModel::updateMobile,
|
||||
label = { Text("شماره موبایل") },
|
||||
placeholder = { Text("09xxxxxxxxx") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.mobile,
|
||||
shape = MaterialTheme.shapes.large,
|
||||
onValueChange = { viewModel.updateMobile(it) },
|
||||
label = { Text("شماره موبایل" , textAlign = TextAlign.Right ) },
|
||||
placeholder = { Text("09xxxxxxxxx") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(MaterialTheme.dime.md))
|
||||
PrimaryButton(text = "ارسال کد", isLoading = ui.busy, onClick = viewModel::sendOtp)
|
||||
PrimaryButton(text = "ارسال کد", isLoading = ui.busy, onClick = { viewModel.sendOtp() })
|
||||
}
|
||||
|
||||
AccountPhase.EnterOtp -> {
|
||||
@@ -100,15 +105,15 @@ fun AccountSheet(
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
OutlinedTextField(
|
||||
value = viewModel.otp,
|
||||
onValueChange = viewModel::updateOtp,
|
||||
onValueChange = { viewModel.updateOtp(it) },
|
||||
label = { Text("کد تأیید") },
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.md))
|
||||
PrimaryButton(text = "تأیید و ورود", isLoading = ui.busy, onClick = viewModel::verifyOtp)
|
||||
TextButton(onClick = viewModel::editMobile, enabled = !ui.busy) {
|
||||
PrimaryButton(text = "تأیید و ورود", isLoading = ui.busy, onClick = { viewModel.verifyOtp() })
|
||||
TextButton(onClick = { viewModel.editMobile() }, enabled = !ui.busy) {
|
||||
Text("ویرایش شماره موبایل")
|
||||
}
|
||||
}
|
||||
@@ -123,7 +128,9 @@ fun AccountSheet(
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
when {
|
||||
ui.productsLoading -> Loading(
|
||||
modifier = Modifier.fillMaxWidth().height(160.dp)
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(160.dp)
|
||||
)
|
||||
else -> Column(
|
||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
||||
@@ -156,7 +163,7 @@ fun AccountSheet(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(Modifier.height(MaterialTheme.dime.lg))
|
||||
TextButton(onClick = viewModel::logout) { Text("خروج از حساب") }
|
||||
TextButton(onClick = { viewModel.logout() }) { Text("خروج از حساب") }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,11 +108,16 @@ private val DarkColorScheme = darkColorScheme(
|
||||
|
||||
@Composable
|
||||
fun DrugTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
themeMode: ThemeMode = ThemeMode.SYSTEM,
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = false,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val darkTheme = when (themeMode) {
|
||||
ThemeMode.SYSTEM -> isSystemInDarkTheme()
|
||||
ThemeMode.DARK -> true
|
||||
ThemeMode.LIGHT -> false
|
||||
}
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.approagency.pharmacy.ui.theme
|
||||
|
||||
/** حالت نمایش انتخابی کاربر؛ پیشفرض پیروی از سیستم است. */
|
||||
enum class ThemeMode { SYSTEM, LIGHT, DARK }
|
||||
Reference in New Issue
Block a user