feat: clean code
This commit is contained in:
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="21" />
|
<bytecodeTargetLevel target="17" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -3,12 +3,18 @@ package com.approagency.pharmacy
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import com.approagency.pharmacy.data.local.database.LabDatabase
|
import com.approagency.pharmacy.data.local.database.LabDatabase
|
||||||
import com.approagency.pharmacy.di.appModule
|
import com.approagency.pharmacy.di.appModule
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
|
|
||||||
class DrugApp : Application(){
|
class DrugApp : Application() {
|
||||||
|
|
||||||
|
// اسکوپ سطحاپ برای کارهای پسزمینهی طولعمرِ برنامه (بهجای GlobalScope).
|
||||||
|
private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
startKoin {
|
startKoin {
|
||||||
@@ -17,8 +23,8 @@ class DrugApp : Application(){
|
|||||||
printLogger()
|
printLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload database (optional, for faster access)
|
// پیشبارگذاری دیتابیس برای دسترسی سریعتر
|
||||||
GlobalScope.launch {
|
applicationScope.launch {
|
||||||
LabDatabase.getInstance(this@DrugApp)
|
LabDatabase.getInstance(this@DrugApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import com.approagency.pharmacy.data.local.SessionManager
|
|||||||
import com.approagency.pharmacy.domain.repository.AuthRepository
|
import com.approagency.pharmacy.domain.repository.AuthRepository
|
||||||
import com.approagency.pharmacy.navigation.AppNavGraph
|
import com.approagency.pharmacy.navigation.AppNavGraph
|
||||||
import com.approagency.pharmacy.presentation.account.OtpAutoFillBus
|
import com.approagency.pharmacy.presentation.account.OtpAutoFillBus
|
||||||
|
import com.approagency.pharmacy.presentation.account.OtpAutofillController
|
||||||
import com.approagency.pharmacy.ui.theme.DrugTheme
|
import com.approagency.pharmacy.ui.theme.DrugTheme
|
||||||
import com.google.android.gms.auth.api.phone.SmsRetriever
|
import com.google.android.gms.auth.api.phone.SmsRetriever
|
||||||
import com.google.android.gms.common.api.CommonStatusCodes
|
import com.google.android.gms.common.api.CommonStatusCodes
|
||||||
@@ -27,7 +28,7 @@ import com.google.android.gms.common.api.Status
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity(), OtpAutofillController {
|
||||||
|
|
||||||
private val session: SessionManager by inject()
|
private val session: SessionManager by inject()
|
||||||
private val authRepository: AuthRepository by inject()
|
private val authRepository: AuthRepository by inject()
|
||||||
@@ -71,7 +72,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
// ---------- خودکارپُرکُنِ کد پیامک (SMS User Consent API) ----------
|
// ---------- خودکارپُرکُنِ کد پیامک (SMS User Consent API) ----------
|
||||||
|
|
||||||
/** آغاز گوشدادن به پیامکِ کد؛ کدِ یافتشده از طریق [OtpAutoFillBus] تحویل میشود. */
|
/** آغاز گوشدادن به پیامکِ کد؛ کدِ یافتشده از طریق [OtpAutoFillBus] تحویل میشود. */
|
||||||
fun startOtpAutofill() {
|
override fun startOtpAutofill() {
|
||||||
SmsRetriever.getClient(this).startSmsUserConsent(null)
|
SmsRetriever.getClient(this).startSmsUserConsent(null)
|
||||||
if (otpSmsReceiver != null) return
|
if (otpSmsReceiver != null) return
|
||||||
val receiver = object : BroadcastReceiver() {
|
val receiver = object : BroadcastReceiver() {
|
||||||
@@ -103,7 +104,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stopOtpAutofill() {
|
override fun stopOtpAutofill() {
|
||||||
otpSmsReceiver?.let { runCatching { unregisterReceiver(it) } }
|
otpSmsReceiver?.let { runCatching { unregisterReceiver(it) } }
|
||||||
otpSmsReceiver = null
|
otpSmsReceiver = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.approagency.pharmacy.data.local
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import com.approagency.pharmacy.ui.theme.ThemeMode
|
import com.approagency.pharmacy.domain.model.ThemeMode
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
|||||||
@@ -37,10 +37,8 @@ class DrugRepositoryImpl(
|
|||||||
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
|
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
|
||||||
return try {
|
return try {
|
||||||
val response= apiService.getDrugDetail(cod = cod)
|
val response= apiService.getDrugDetail(cod = cod)
|
||||||
println(response.message)
|
|
||||||
return Result.success(response)
|
return Result.success(response)
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
println(e.message)
|
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +46,6 @@ class DrugRepositoryImpl(
|
|||||||
override suspend fun getGorohDaroei(): Result<DarmanModel> {
|
override suspend fun getGorohDaroei(): Result<DarmanModel> {
|
||||||
return try {
|
return try {
|
||||||
val response = apiService.getGorohDaroei()
|
val response = apiService.getGorohDaroei()
|
||||||
println(response)
|
|
||||||
return Result.success(response)
|
return Result.success(response)
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
@@ -63,8 +60,6 @@ class DrugRepositoryImpl(
|
|||||||
pageNumber = params.pageNumber
|
pageNumber = params.pageNumber
|
||||||
)
|
)
|
||||||
|
|
||||||
println("HTML Response length: ${htmlResponse.length}")
|
|
||||||
|
|
||||||
val searchResult = parser.parseSearchResultsWithPagination(htmlResponse)
|
val searchResult = parser.parseSearchResultsWithPagination(htmlResponse)
|
||||||
|
|
||||||
Result.success(searchResult)
|
Result.success(searchResult)
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
package com.approagency.pharmacy.ui.theme
|
package com.approagency.pharmacy.domain.model
|
||||||
|
|
||||||
/** حالت نمایش انتخابی کاربر؛ پیشفرض پیروی از سیستم است. */
|
/** حالت نمایش انتخابی کاربر؛ پیشفرض پیروی از سیستم است. */
|
||||||
enum class ThemeMode { SYSTEM, LIGHT, DARK }
|
enum class ThemeMode { SYSTEM, LIGHT, DARK }
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.approagency.pharmacy.navigation
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
|
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.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import com.approagency.pharmacy.data.local.AccountState
|
||||||
|
|
||||||
|
/**
|
||||||
|
* نوار بالای اپ: دکمهی منو، اطلاعات حساب (موبایل + وضعیت اشتراک) و کنشِ
|
||||||
|
* ورود/خرید اشتراک.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun AccountAppBar(
|
||||||
|
account: AccountState,
|
||||||
|
onMenuClick: () -> Unit,
|
||||||
|
onAccountClick: () -> Unit
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onMenuClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Menu,
|
||||||
|
contentDescription = "منو"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
if (account.isLoggedIn) {
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = account.mobile ?: account.displayName.orEmpty(),
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = account.subscriptionTitle ?: "بدون اشتراک",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = if (account.isSubscribed)
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text(text = "دارویاب", style = MaterialTheme.typography.titleMedium)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
if (!account.isLoggedIn) {
|
||||||
|
ElevatedButton(onClick = onAccountClick) {
|
||||||
|
Text("ورود", textAlign = TextAlign.Center)
|
||||||
|
}
|
||||||
|
} else if (!account.isSubscribed) {
|
||||||
|
ElevatedButton(onClick = onAccountClick) {
|
||||||
|
Text("خرید اشتراک", textAlign = TextAlign.Center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,22 +1,11 @@
|
|||||||
package com.approagency.pharmacy.navigation
|
package com.approagency.pharmacy.navigation
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.padding
|
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.DrawerValue
|
||||||
import androidx.compose.material3.ElevatedButton
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
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.ModalNavigationDrawer
|
||||||
import androidx.compose.material3.Scaffold
|
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.material3.rememberDrawerState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@@ -27,9 +16,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.approagency.pharmacy.data.local.AccountState
|
|
||||||
import com.approagency.pharmacy.data.local.SessionManager
|
import com.approagency.pharmacy.data.local.SessionManager
|
||||||
import com.approagency.pharmacy.domain.repository.AuthRepository
|
import com.approagency.pharmacy.domain.repository.AuthRepository
|
||||||
import com.approagency.pharmacy.presentation.account.AccountDrawer
|
import com.approagency.pharmacy.presentation.account.AccountDrawer
|
||||||
@@ -112,53 +99,3 @@ fun MainContainer(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@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 {
|
|
||||||
Text(
|
|
||||||
text = account.mobile ?: account.displayName.orEmpty(),
|
|
||||||
style = MaterialTheme.typography.bodyMedium
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = account.subscriptionTitle ?: "بدون اشتراک",
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
color = if (account.isSubscribed)
|
|
||||||
MaterialTheme.colorScheme.primary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text(text = "دارویاب", style = MaterialTheme.typography.titleMedium)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
if (!account.isLoggedIn) {
|
|
||||||
ElevatedButton(onClick = onAccountClick) {
|
|
||||||
Text("ورود", textAlign = TextAlign.Center)
|
|
||||||
}
|
|
||||||
}else if (!account.isSubscribed){
|
|
||||||
ElevatedButton(onClick = onAccountClick) {
|
|
||||||
Text("خرید اشتراک", textAlign = TextAlign.Center)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.approagency.pharmacy.data.local.AccountState
|
import com.approagency.pharmacy.data.local.AccountState
|
||||||
import com.approagency.pharmacy.ui.theme.ThemeMode
|
import com.approagency.pharmacy.domain.model.ThemeMode
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
@@ -26,7 +24,6 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
@@ -35,12 +32,9 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
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.CustomModalBottomSheet
|
||||||
import com.approagency.pharmacy.presentation.common.OtpTextField
|
import com.approagency.pharmacy.presentation.common.OtpTextField
|
||||||
import com.approagency.pharmacy.presentation.common.PrimaryButton
|
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.AccountPhase
|
||||||
import com.approagency.pharmacy.presentation.viewModel.AccountViewModel
|
import com.approagency.pharmacy.presentation.viewModel.AccountViewModel
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
@@ -71,9 +65,9 @@ fun AccountSheet(
|
|||||||
// در مرحلهی کد، گوشدادن به پیامک را آغاز کن و با خروج متوقفش کن.
|
// در مرحلهی کد، گوشدادن به پیامک را آغاز کن و با خروج متوقفش کن.
|
||||||
val isOtpStep = ui.phase == AccountPhase.EnterOtp
|
val isOtpStep = ui.phase == AccountPhase.EnterOtp
|
||||||
DisposableEffect(isOtpStep) {
|
DisposableEffect(isOtpStep) {
|
||||||
val mainActivity = activity as? MainActivity
|
val autofill = activity as? OtpAutofillController
|
||||||
if (isOtpStep) mainActivity?.startOtpAutofill()
|
if (isOtpStep) autofill?.startOtpAutofill()
|
||||||
onDispose { mainActivity?.stopOtpAutofill() }
|
onDispose { autofill?.stopOtpAutofill() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// کدِ خواندهشده از پیامک را در فیلد بگذار و بهصورت خودکار تأیید کن.
|
// کدِ خواندهشده از پیامک را در فیلد بگذار و بهصورت خودکار تأیید کن.
|
||||||
@@ -151,13 +145,13 @@ fun AccountSheet(
|
|||||||
ui.productsLoading -> Column(
|
ui.productsLoading -> Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
||||||
) {
|
) {
|
||||||
repeat(2) { ProductCardSkeleton() }
|
repeat(2) { SubscriptionProductCardSkeleton() }
|
||||||
}
|
}
|
||||||
else -> Column(
|
else -> Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.md)
|
||||||
) {
|
) {
|
||||||
ui.products.forEach { product ->
|
ui.products.forEach { product ->
|
||||||
ProductCard(
|
SubscriptionProductCard(
|
||||||
product = product,
|
product = product,
|
||||||
isPurchasing = ui.purchasingProductId == product.id,
|
isPurchasing = ui.purchasingProductId == product.id,
|
||||||
onBuy = { activity?.let { viewModel.purchase(it, product) } }
|
onBuy = { activity?.let { viewModel.purchase(it, product) } }
|
||||||
@@ -205,74 +199,3 @@ private fun SheetTitle(text: String) {
|
|||||||
Text(text = text, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
|
Text(text = text, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold)
|
||||||
Spacer(Modifier.height(MaterialTheme.dime.xs))
|
Spacer(Modifier.height(MaterialTheme.dime.xs))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun ProductCard(
|
|
||||||
product: SubscriptionProduct,
|
|
||||||
isPurchasing: Boolean,
|
|
||||||
onBuy: () -> Unit
|
|
||||||
) {
|
|
||||||
Card(modifier = Modifier.fillMaxWidth()) {
|
|
||||||
Column(modifier = Modifier.padding(MaterialTheme.dime.lg)) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(product.title, style = MaterialTheme.typography.titleMedium)
|
|
||||||
Text(
|
|
||||||
"${formatPrice(product.price)} تومان",
|
|
||||||
style = MaterialTheme.typography.titleSmall,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (!product.description.isNullOrBlank()) {
|
|
||||||
Spacer(Modifier.height(MaterialTheme.dime.xs))
|
|
||||||
Text(
|
|
||||||
product.description,
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(Modifier.height(MaterialTheme.dime.md))
|
|
||||||
PrimaryButton(text = "خرید", height = 44, isLoading = isPurchasing, onClick = onBuy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** جایگیرندهی 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()
|
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package com.approagency.pharmacy.presentation.account
|
||||||
|
|
||||||
|
/**
|
||||||
|
* قراردادِ شروع/توقفِ خودکارپُرکُنِ کدِ پیامک.
|
||||||
|
*
|
||||||
|
* اکتیویتی میزبان آن را پیادهسازی میکند؛ شیتِ ورود فقط به این اینترفیس وابسته
|
||||||
|
* است و از نوعِ مشخصِ اکتیویتی بیخبر میماند.
|
||||||
|
*/
|
||||||
|
interface OtpAutofillController {
|
||||||
|
fun startOtpAutofill()
|
||||||
|
fun stopOtpAutofill()
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package com.approagency.pharmacy.presentation.account
|
||||||
|
|
||||||
|
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.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
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.unit.dp
|
||||||
|
import com.approagency.pharmacy.domain.model.SubscriptionProduct
|
||||||
|
import com.approagency.pharmacy.presentation.common.PrimaryButton
|
||||||
|
import com.approagency.pharmacy.presentation.common.shimmer
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
/** کارت یک محصول اشتراک بههمراه دکمهی خرید. */
|
||||||
|
@Composable
|
||||||
|
fun SubscriptionProductCard(
|
||||||
|
product: SubscriptionProduct,
|
||||||
|
isPurchasing: Boolean,
|
||||||
|
onBuy: () -> Unit
|
||||||
|
) {
|
||||||
|
Card(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Column(modifier = Modifier.padding(MaterialTheme.dime.lg)) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(product.title, style = MaterialTheme.typography.titleMedium)
|
||||||
|
Text(
|
||||||
|
"${formatPrice(product.price)} تومان",
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!product.description.isNullOrBlank()) {
|
||||||
|
Spacer(Modifier.height(MaterialTheme.dime.xs))
|
||||||
|
Text(
|
||||||
|
product.description,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(MaterialTheme.dime.md))
|
||||||
|
PrimaryButton(text = "خرید", height = 44, isLoading = isPurchasing, onClick = onBuy)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** جایگیرندهی shimmer برای کارت محصول هنگام بارگذاری. */
|
||||||
|
@Composable
|
||||||
|
fun SubscriptionProductCardSkeleton() {
|
||||||
|
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()
|
||||||
@@ -65,7 +65,6 @@ class HomeViewModel (
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
||||||
println(result)
|
|
||||||
_uiState.update { it.copy(
|
_uiState.update { it.copy(
|
||||||
drugSearchState = it.drugSearchState.copy(
|
drugSearchState = it.drugSearchState.copy(
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
@@ -74,10 +73,8 @@ class HomeViewModel (
|
|||||||
|
|
||||||
) }
|
) }
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println(e.message)
|
|
||||||
handleError(e.toUserMessage())
|
handleError(e.toUserMessage())
|
||||||
}catch (e: Exception) {
|
}catch (e: Exception) {
|
||||||
println(e.message)
|
|
||||||
handleError(e.toUserMessage())
|
handleError(e.toUserMessage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +100,6 @@ class HomeViewModel (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
println(result)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
handleError(e.toUserMessage())
|
handleError(e.toUserMessage())
|
||||||
}catch (e: Exception) {
|
}catch (e: Exception) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import com.approagency.pharmacy.domain.model.ThemeMode
|
||||||
import com.fabirt.podcastapp.ui.theme.Shapes
|
import com.fabirt.podcastapp.ui.theme.Shapes
|
||||||
import com.vada.caller.ui.theme.Dime
|
import com.vada.caller.ui.theme.Dime
|
||||||
import com.vada.caller.ui.theme.LocalDime
|
import com.vada.caller.ui.theme.LocalDime
|
||||||
|
|||||||
Reference in New Issue
Block a user