diff --git a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoDialog.kt
similarity index 51%
rename from core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt
rename to core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoDialog.kt
index dcc3dfee1..2d671d283 100644
--- a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoMarketCapitalizationDialog.kt
+++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/ui/InfoDialog.kt
@@ -15,7 +15,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -25,15 +24,25 @@ import dev.arkbuilders.rate.core.presentation.R
import dev.arkbuilders.rate.core.presentation.theme.ArkColor
@Composable
-fun InfoMarketCapitalizationDialog(onDismiss: () -> Unit) {
+fun InfoDialog(
+ title: String,
+ desc: String,
+ onDismiss: () -> Unit,
+) {
Dialog(onDismissRequest = { onDismiss() }) {
- InfoMarketCapitalizationDialogContent(onDismiss)
+ Content(title, desc, onDismiss)
}
}
-@Preview
+@Preview(showBackground = true)
@Composable
-private fun InfoMarketCapitalizationDialogContent(onDismiss: () -> Unit = {}) {
+private fun Content(
+ title: String = "Currency Already Selected",
+ desc: String =
+ "Please choose a different currency to complete the pairing. " +
+ "Identical currencies cannot be paired together.",
+ onDismiss: () -> Unit = {},
+) {
Column(
modifier =
Modifier
@@ -59,7 +68,7 @@ private fun InfoMarketCapitalizationDialogContent(onDismiss: () -> Unit = {}) {
onClick = { onDismiss() },
) {
Icon(
- modifier = Modifier,
+ modifier = Modifier.size(12.dp),
painter = painterResource(id = R.drawable.ic_close),
contentDescription = "",
tint = ArkColor.FGQuinary,
@@ -68,73 +77,15 @@ private fun InfoMarketCapitalizationDialogContent(onDismiss: () -> Unit = {}) {
}
Text(
modifier = Modifier.padding(top = 12.dp, start = 16.dp, end = 16.dp),
- text = "Market Capitalization",
+ text = title,
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = ArkColor.TextPrimary,
)
Text(
modifier = Modifier.padding(top = 4.dp, start = 16.dp, end = 16.dp, bottom = 36.dp),
- text = stringResource(id = R.string.info_dialog_market_capitalization_description),
- fontSize = 18.sp,
- color = ArkColor.TextTertiary,
- )
- }
-}
-
-@Composable
-fun InfoValueOfCirculatingDialog(onDismiss: () -> Unit) {
- Dialog(onDismissRequest = { onDismiss() }) {
- InfoValueOfCirculatingDialogContent(onDismiss)
- }
-}
-
-@Preview
-@Composable
-private fun InfoValueOfCirculatingDialogContent(onDismiss: () -> Unit = {}) {
- Column(
- modifier =
- Modifier
- .fillMaxWidth()
- .background(Color.White, RoundedCornerShape(12.dp)),
- ) {
- Box(modifier = Modifier.fillMaxWidth()) {
- Icon(
- modifier =
- Modifier
- .padding(top = 20.dp, start = 16.dp)
- .align(Alignment.TopStart),
- painter = painterResource(id = R.drawable.ic_info_bg),
- contentDescription = "",
- tint = Color.Unspecified,
- )
- IconButton(
- modifier =
- Modifier
- .size(44.dp)
- .padding(top = 12.dp, end = 12.dp)
- .align(Alignment.TopEnd),
- onClick = { onDismiss() },
- ) {
- Icon(
- modifier = Modifier,
- painter = painterResource(id = R.drawable.ic_close),
- contentDescription = "",
- tint = ArkColor.FGQuinary,
- )
- }
- }
- Text(
- modifier = Modifier.padding(top = 12.dp, start = 16.dp, end = 16.dp),
- text = "Value of Circulating Currency",
- fontWeight = FontWeight.SemiBold,
- fontSize = 18.sp,
- color = ArkColor.TextPrimary,
- )
- Text(
- modifier = Modifier.padding(top = 4.dp, start = 16.dp, end = 16.dp, bottom = 36.dp),
- text = stringResource(id = R.string.info_dialog_value_of_circulating_description),
- fontSize = 18.sp,
+ text = desc,
+ fontSize = 14.sp,
color = ArkColor.TextTertiary,
)
}
diff --git a/core/presentation/src/main/res/values/strings.xml b/core/presentation/src/main/res/values/strings.xml
index 36cb9383f..aaabf886f 100644
--- a/core/presentation/src/main/res/values/strings.xml
+++ b/core/presentation/src/main/res/values/strings.xml
@@ -2,7 +2,9 @@
ARK Rate
ARK Rate [Debug]
+ Market Capitalization
The total market value of a cryptocurrency\'s circulating supply.\n\nIt is analogous to the free-float capitalization in the stock market.\n\nCap = Current Price x Circulating Supply.
+ Value of Circulating Currency
Currency in circulation refers to the amount of cash–in the form of paper notes or coins–within a country that is physically used to conduct transactions between consumers and businesses.
Portfolios
@@ -64,6 +66,9 @@
Latest rates refresh
Latest alerts check
+ Currency already selected
+ Please choose a different currency. Identical currencies cannot be used together.
+
Create Group
Please enter a name for this group.
Group name
diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt
index 719227c14..b042e699b 100644
--- a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt
+++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertScreen.kt
@@ -111,6 +111,22 @@ fun AddPairAlertScreen(
)
AppSharedFlow.ShowAddedSnackbarPairAlert.flow.emit(visuals)
}
+
+ is AddPairAlertScreenEffect.NavigateSearchBase ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.AddPairAlertBase.name,
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
+
+ is AddPairAlertScreenEffect.NavigateSearchTarget ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.AddPairAlertTarget.name,
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
}
}
@@ -127,7 +143,17 @@ fun AddPairAlertScreen(
},
) {
Box(modifier = Modifier.padding(it)) {
- Content(state, navigator, viewModel)
+ Content(
+ state = state,
+ navigateSearchBase = viewModel::onNavigateSearchBase,
+ navigateSearchTarget = viewModel::onNavigateSearchTarget,
+ onGroupSelect = viewModel::onGroupSelect,
+ onPriceOrPercentChanged = viewModel::onPriceOrPercentChanged,
+ onOneTimeChanged = viewModel::onOneTimeChanged,
+ onPriceOrPercentInputChanged = viewModel::onPriceOrPercentInputChanged,
+ onIncreaseToggle = viewModel::onIncreaseToggle,
+ onSaveClick = viewModel::onSaveClick,
+ )
}
}
}
@@ -135,8 +161,14 @@ fun AddPairAlertScreen(
@Composable
private fun Content(
state: AddPairAlertScreenState,
- navigator: DestinationsNavigator,
- viewModel: AddPairAlertViewModel,
+ navigateSearchBase: () -> Unit,
+ navigateSearchTarget: () -> Unit,
+ onGroupSelect: (String) -> Unit,
+ onPriceOrPercentChanged: (Boolean) -> Unit,
+ onOneTimeChanged: (Boolean) -> Unit,
+ onPriceOrPercentInputChanged: (String) -> Unit,
+ onIncreaseToggle: () -> Unit,
+ onSaveClick: () -> Unit,
) {
var showNewGroupDialog by remember { mutableStateOf(false) }
var showGroupsPopup by remember { mutableStateOf(false) }
@@ -144,7 +176,7 @@ private fun Content(
if (showNewGroupDialog) {
GroupCreateDialog(onDismiss = { showNewGroupDialog = false }) {
- viewModel.onGroupSelect(it)
+ onGroupSelect(it)
}
}
@@ -155,12 +187,18 @@ private fun Content(
.weight(1f)
.verticalScroll(rememberScrollState()),
) {
- PriceOrPercent(state, viewModel::onPriceOrPercentChanged)
- EditCondition(state, viewModel, navigator)
+ PriceOrPercent(state, onPriceOrPercentChanged)
+ EditCondition(
+ state = state,
+ navigateSearchBase = navigateSearchBase,
+ navigateSearchTarget = navigateSearchTarget,
+ onPriceOrPercentInputChanged = onPriceOrPercentInputChanged,
+ onIncreaseToggle = onIncreaseToggle,
+ )
OneTimeOrRecurrent(
state.priceOrPercent.isLeft(),
state.oneTimeNotRecurrent,
- viewModel::onOneTimeChanged,
+ onOneTimeChanged,
)
DropDownWithIcon(
modifier =
@@ -192,7 +230,7 @@ private fun Content(
GroupSelectPopup(
groups = state.availableGroups,
widthPx = addGroupBtnWidth,
- onGroupSelect = { viewModel.onGroupSelect(it) },
+ onGroupSelect = onGroupSelect,
onNewGroupClick = { showNewGroupDialog = true },
onDismiss = { showGroupsPopup = false },
)
@@ -207,7 +245,7 @@ private fun Content(
Modifier
.fillMaxWidth()
.padding(16.dp),
- onClick = { viewModel.onSaveClick() },
+ onClick = onSaveClick,
enabled = state.finishEnabled,
) {
Text(
@@ -344,8 +382,10 @@ private fun DropDownBtn(
@Composable
private fun EditCondition(
state: AddPairAlertScreenState,
- viewModel: AddPairAlertViewModel,
- navigator: DestinationsNavigator,
+ navigateSearchBase: () -> Unit,
+ navigateSearchTarget: () -> Unit,
+ onPriceOrPercentInputChanged: (String) -> Unit,
+ onIncreaseToggle: () -> Unit,
) {
val ctx = LocalContext.current
Column(
@@ -368,9 +408,7 @@ private fun EditCondition(
modifier = Modifier.padding(start = 8.dp),
title = state.targetCode,
) {
- navigator.navigate(
- SearchCurrencyScreenDestination(AppSharedFlowKey.AddPairAlertTarget.name),
- )
+ navigateSearchTarget()
}
Text(
modifier = Modifier.padding(start = 8.dp),
@@ -385,7 +423,7 @@ private fun EditCondition(
if (state.oneTimeNotRecurrent && state.priceOrPercent.isLeft())
this
else
- clickable { viewModel.onIncreaseToggle() }
+ clickable { onIncreaseToggle() }
},
verticalAlignment = Alignment.CenterVertically,
) {
@@ -448,7 +486,7 @@ private fun EditCondition(
ifLeft = { it },
ifRight = { it },
),
- onValueChange = { viewModel.onPriceOrPercentInputChanged(it) },
+ onValueChange = { onPriceOrPercentInputChanged(it) },
)
if (state.priceOrPercent.isLeft()) {
Text(
@@ -488,9 +526,7 @@ private fun EditCondition(
modifier = Modifier.padding(start = 16.dp),
title = state.baseCode,
) {
- navigator.navigate(
- SearchCurrencyScreenDestination(AppSharedFlowKey.AddPairAlertBase.name),
- )
+ navigateSearchBase()
}
}
}
diff --git a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt
index 1801f9c4c..c3c7a4291 100644
--- a/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt
+++ b/feature/pairalert/src/main/java/dev/arkbuilders/rate/feature/pairalert/presentation/add/AddPairAlertViewModel.kt
@@ -47,6 +47,14 @@ sealed class AddPairAlertScreenEffect {
data object NavigateBack : AddPairAlertScreenEffect()
class NotifyPairAdded(val pair: PairAlert) : AddPairAlertScreenEffect()
+
+ data class NavigateSearchTarget(
+ val prohibitedCodes: List,
+ ) : AddPairAlertScreenEffect()
+
+ data class NavigateSearchBase(
+ val prohibitedCodes: List,
+ ) : AddPairAlertScreenEffect()
}
class AddPairAlertViewModel(
@@ -346,6 +354,18 @@ class AddPairAlertViewModel(
reduce { state.copy(finishEnabled = enabled) }
}
+ fun onNavigateSearchBase() =
+ intent {
+ val prohibitedCodes = listOf(state.targetCode)
+ postSideEffect(AddPairAlertScreenEffect.NavigateSearchBase(prohibitedCodes))
+ }
+
+ fun onNavigateSearchTarget() =
+ intent {
+ val prohibitedCodes = listOf(state.baseCode)
+ postSideEffect(AddPairAlertScreenEffect.NavigateSearchTarget(prohibitedCodes))
+ }
+
companion object {
private val INITIAL_ONE_TIME_SCALE = BigDecimal.valueOf(1.1)
private val INITIAL_RECURRENT_SCALE = BigDecimal.valueOf(10)
diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt
index 18c56c1ed..c473eedf9 100644
--- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt
+++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetScreen.kt
@@ -100,6 +100,23 @@ fun AddAssetScreen(navigator: DestinationsNavigator) {
),
)
}
+
+ is AddAssetSideEffect.NavigateSearchAdd ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.AddAsset.toString(),
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
+
+ is AddAssetSideEffect.NavigateSearchSet ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.SetAssetCode.name,
+ pos = effect.index,
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
}
}
@@ -115,18 +132,10 @@ fun AddAssetScreen(navigator: DestinationsNavigator) {
Content(
state = state,
onAssetValueChanged = viewModel::onAssetValueChange,
- onNewCurrencyClick = {
- navigator.navigate(
- SearchCurrencyScreenDestination(AppSharedFlowKey.AddAsset.toString()),
- )
- },
+ onNewCurrencyClick = viewModel::onAddCode,
onAssetRemove = viewModel::onAssetRemove,
onGroupSelect = viewModel::onGroupSelect,
- onCodeChange = {
- navigator.navigate(
- SearchCurrencyScreenDestination(AppSharedFlowKey.SetAssetCode.name, it),
- )
- },
+ onCodeChange = viewModel::onSetCode,
onAddAsset = viewModel::onAddAsset,
)
}
diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt
index a333b286e..7cbb4bd67 100644
--- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt
+++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/add/AddAssetViewModel.kt
@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import dev.arkbuilders.rate.core.domain.CurrUtils
import dev.arkbuilders.rate.core.domain.model.AmountStr
+import dev.arkbuilders.rate.core.domain.model.CurrencyCode
import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager
import dev.arkbuilders.rate.core.domain.repo.CodeUseStatRepo
import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo
@@ -37,6 +38,11 @@ sealed class AddAssetSideEffect {
AddAssetSideEffect()
data object NavigateBack : AddAssetSideEffect()
+
+ data class NavigateSearchAdd(val prohibitedCodes: List) : AddAssetSideEffect()
+
+ data class NavigateSearchSet(val index: Int, val prohibitedCodes: List) :
+ AddAssetSideEffect()
}
class AddAssetViewModel(
@@ -139,6 +145,19 @@ class AddAssetViewModel(
postSideEffect(AddAssetSideEffect.NotifyAssetAdded(currencies))
postSideEffect(AddAssetSideEffect.NavigateBack)
}
+
+ fun onSetCode(index: Int) =
+ intent {
+ val prohibitedCodes = state.currencies.map { it.code }.toMutableList()
+ prohibitedCodes.removeAt(index)
+ postSideEffect(AddAssetSideEffect.NavigateSearchSet(index, prohibitedCodes))
+ }
+
+ fun onAddCode() =
+ intent {
+ val prohibitedCodes = state.currencies.map { it.code }
+ postSideEffect(AddAssetSideEffect.NavigateSearchAdd(prohibitedCodes))
+ }
}
@PortfolioScope
diff --git a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt
index 664a9d3e2..5901509f9 100644
--- a/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt
+++ b/feature/portfolio/src/main/java/dev/arkbuilders/rate/feature/portfolio/presentation/edit/EditAssetScreen.kt
@@ -48,8 +48,7 @@ import dev.arkbuilders.rate.core.presentation.theme.ArkColor
import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv
import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack
import dev.arkbuilders.rate.core.presentation.ui.ArkCursorLargeTextField
-import dev.arkbuilders.rate.core.presentation.ui.InfoMarketCapitalizationDialog
-import dev.arkbuilders.rate.core.presentation.ui.InfoValueOfCirculatingDialog
+import dev.arkbuilders.rate.core.presentation.ui.InfoDialog
import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen
import dev.arkbuilders.rate.feature.portfolio.di.PortfolioComponentHolder
import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination
@@ -114,11 +113,19 @@ private fun Content(
}
if (showMarketCapitalizationDialog) {
- InfoMarketCapitalizationDialog { showMarketCapitalizationDialog = false }
+ InfoDialog(
+ title = stringResource(id = CoreRString.info_dialog_market_capitalization),
+ desc = stringResource(id = CoreRString.info_dialog_market_capitalization_description),
+ onDismiss = { showMarketCapitalizationDialog = false },
+ )
}
if (showValueOfCirculatingDialog) {
- InfoValueOfCirculatingDialog { showValueOfCirculatingDialog = false }
+ InfoDialog(
+ title = stringResource(id = CoreRString.info_dialog_value_of_circulating),
+ desc = stringResource(id = CoreRString.info_dialog_value_of_circulating_description),
+ onDismiss = { showValueOfCirculatingDialog = false },
+ )
}
Column(
diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt
index d00822d35..9ffcc8638 100644
--- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt
+++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickScreen.kt
@@ -116,6 +116,23 @@ fun AddQuickScreen(
),
)
}
+
+ is AddQuickScreenEffect.NavigateSearchAdd ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.AddQuickCode.toString(),
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
+
+ is AddQuickScreenEffect.NavigateSearchSet ->
+ navigator.navigate(
+ SearchCurrencyScreenDestination(
+ appSharedFlowKeyString = AppSharedFlowKey.SetQuickCode.toString(),
+ pos = effect.index,
+ prohibitedCodes = effect.prohibitedCodes.toTypedArray(),
+ ),
+ )
}
}
Scaffold(
@@ -135,21 +152,10 @@ fun AddQuickScreen(
Content(
state = state,
onAmountChanged = viewModel::onAssetAmountChange,
- onNewCurrencyClick = {
- navigator.navigate(
- SearchCurrencyScreenDestination(AppSharedFlowKey.AddQuickCode.toString()),
- )
- },
+ onNewCurrencyClick = viewModel::onAddCode,
onCurrencyRemove = viewModel::onCurrencyRemove,
onGroupSelect = viewModel::onGroupSelect,
- onCodeChange = { index ->
- navigator.navigate(
- SearchCurrencyScreenDestination(
- AppSharedFlowKey.SetQuickCode.toString(),
- index,
- ),
- )
- },
+ onCodeChange = viewModel::onSetCode,
onAddAsset = viewModel::onAddQuickPair,
)
}
diff --git a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt
index 3c28d4f54..64dc63ae6 100644
--- a/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt
+++ b/feature/quick/src/main/java/dev/arkbuilders/rate/feature/quick/presentation/add/AddQuickViewModel.kt
@@ -42,6 +42,12 @@ sealed class AddQuickScreenEffect {
data class NotifyPairAdded(val pair: QuickPair) : AddQuickScreenEffect()
data object NavigateBack : AddQuickScreenEffect()
+
+ data class NavigateSearchSet(val index: Int, val prohibitedCodes: List) :
+ AddQuickScreenEffect()
+
+ data class NavigateSearchAdd(val prohibitedCodes: List) :
+ AddQuickScreenEffect()
}
class AddQuickViewModel(
@@ -199,6 +205,21 @@ class AddQuickViewModel(
state.copy(finishEnabled = finishEnabled)
}
}
+
+ fun onSetCode(index: Int) =
+ intent {
+ val prohibitedCodes =
+ state.currencies.map { it.code }.toMutableList().apply {
+ removeAt(index)
+ }
+ postSideEffect(AddQuickScreenEffect.NavigateSearchSet(index, prohibitedCodes))
+ }
+
+ fun onAddCode() =
+ intent {
+ val prohibitedCodes = state.currencies.map { it.code }
+ postSideEffect(AddQuickScreenEffect.NavigateSearchAdd(prohibitedCodes))
+ }
}
class AddQuickViewModelFactory @AssistedInject constructor(
diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyInfoItem.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyInfoItem.kt
new file mode 100644
index 000000000..0d12a319e
--- /dev/null
+++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyInfoItem.kt
@@ -0,0 +1,58 @@
+package dev.arkbuilders.rate.feature.search.presentation
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import dev.arkbuilders.rate.core.presentation.theme.ArkColor
+import dev.arkbuilders.rate.core.presentation.ui.CurrIcon
+
+@Composable
+fun SearchCurrencyInfoItem(
+ model: CurrencySearchModel,
+ onClick: (CurrencySearchModel) -> Unit,
+) {
+ val contentAlpha = if (model.isProhibited) 0.4f else 1f
+ Column {
+ Row(
+ modifier =
+ Modifier
+ .alpha(contentAlpha)
+ .fillMaxWidth()
+ .clickable { onClick(model) }
+ .padding(horizontal = 24.dp, vertical = 16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ CurrIcon(modifier = Modifier.size(40.dp), code = model.code)
+ Column(
+ modifier = Modifier.padding(start = 12.dp),
+ verticalArrangement = Arrangement.Center,
+ ) {
+ Text(
+ text = model.code,
+ fontWeight = FontWeight.Medium,
+ color = ArkColor.TextPrimary,
+ )
+ if (model.name.isNotEmpty()) {
+ Text(text = model.name, color = ArkColor.TextTertiary)
+ }
+ }
+ }
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ thickness = 1.dp,
+ color = ArkColor.BorderSecondary,
+ )
+ }
+}
diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt
index e1e0864da..cdb411607 100644
--- a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt
+++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchCurrencyScreen.kt
@@ -19,11 +19,11 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
-import dev.arkbuilders.rate.core.domain.model.CurrencyName
+import dev.arkbuilders.rate.core.domain.model.CurrencyCode
import dev.arkbuilders.rate.core.presentation.CoreRString
import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv
import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack
-import dev.arkbuilders.rate.core.presentation.ui.CurrencyInfoItem
+import dev.arkbuilders.rate.core.presentation.ui.InfoDialog
import dev.arkbuilders.rate.core.presentation.ui.ListHeader
import dev.arkbuilders.rate.core.presentation.ui.LoadingScreen
import dev.arkbuilders.rate.core.presentation.ui.NoResult
@@ -37,6 +37,7 @@ import org.orbitmvi.orbit.compose.collectSideEffect
fun SearchCurrencyScreen(
appSharedFlowKeyString: String,
pos: Int? = null,
+ prohibitedCodes: Array? = null,
navigator: DestinationsNavigator,
) {
val ctx = LocalContext.current
@@ -48,7 +49,7 @@ fun SearchCurrencyScreen(
viewModel(
factory =
component.searchVMFactory()
- .create(appSharedFlowKeyString, pos),
+ .create(appSharedFlowKeyString, pos, prohibitedCodes?.toList()),
)
val state by viewModel.collectAsState()
@@ -58,6 +59,14 @@ fun SearchCurrencyScreen(
}
}
+ if (state.showCodeProhibitedDialog) {
+ InfoDialog(
+ title = stringResource(CoreRString.search_currency_already_selected),
+ desc = stringResource(CoreRString.search_currency_already_selected_desc),
+ onDismiss = viewModel::onCodeProhibitedDialogDismiss,
+ )
+ }
+
Scaffold(
topBar = {
AppTopBarBack(
@@ -102,18 +111,18 @@ private fun Input(
@Composable
private fun Results(
filter: String,
- frequent: List,
- all: List,
- topResultsFiltered: List,
- onClick: (CurrencyName) -> Unit,
+ frequent: List,
+ all: List,
+ topResultsFiltered: List,
+ onClick: (CurrencySearchModel) -> Unit,
) {
when {
filter.isNotEmpty() -> {
if (topResultsFiltered.isNotEmpty()) {
LazyColumn {
item { ListHeader(stringResource(CoreRString.top_results)) }
- items(topResultsFiltered) { name ->
- CurrencyInfoItem(name) { onClick(it) }
+ items(topResultsFiltered) { model ->
+ SearchCurrencyInfoItem(model) { onClick(it) }
}
}
} else {
@@ -125,13 +134,13 @@ private fun Results(
LazyColumn {
if (frequent.isNotEmpty()) {
item { ListHeader(stringResource(CoreRString.frequent_currencies)) }
- items(frequent) { name ->
- CurrencyInfoItem(name) { onClick(it) }
+ items(frequent) { model ->
+ SearchCurrencyInfoItem(model) { onClick(it) }
}
}
item { ListHeader(stringResource(CoreRString.all_currencies)) }
- items(all) { name ->
- CurrencyInfoItem(name) { onClick(it) }
+ items(all) { model ->
+ SearchCurrencyInfoItem(model) { onClick(it) }
}
}
}
diff --git a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt
index c6c5bd020..6c27eb79b 100644
--- a/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt
+++ b/feature/search/src/main/java/dev/arkbuilders/rate/feature/search/presentation/SearchViewModel.kt
@@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModelProvider
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import dev.arkbuilders.rate.core.domain.model.CurrencyCode
import dev.arkbuilders.rate.core.domain.model.CurrencyName
import dev.arkbuilders.rate.core.domain.repo.AnalyticsManager
import dev.arkbuilders.rate.core.domain.repo.CurrencyRepo
@@ -21,13 +22,20 @@ import org.orbitmvi.orbit.syntax.simple.postSideEffect
import org.orbitmvi.orbit.syntax.simple.reduce
import org.orbitmvi.orbit.viewmodel.container
+data class CurrencySearchModel(
+ val code: CurrencyCode,
+ val name: String,
+ val isProhibited: Boolean,
+)
+
data class SearchScreenState(
- val frequent: List = emptyList(),
- val all: List = emptyList(),
+ val frequent: List = emptyList(),
+ val all: List = emptyList(),
val filter: String = "",
- val topResults: List = emptyList(),
- val topResultsFiltered: List = emptyList(),
+ val topResults: List = emptyList(),
+ val topResultsFiltered: List = emptyList(),
val initialized: Boolean = false,
+ val showCodeProhibitedDialog: Boolean = false,
)
sealed class SearchScreenEffect {
@@ -37,6 +45,7 @@ sealed class SearchScreenEffect {
class SearchViewModel(
private val appSharedFlowKeyString: String,
private val pos: Int?,
+ private val prohibitedCodes: List?,
private val currencyRepo: CurrencyRepo,
private val calcFrequentCurrUseCase: CalcFrequentCurrUseCase,
private val getTopResultUseCase: GetTopResultUseCase,
@@ -50,11 +59,12 @@ class SearchViewModel(
analyticsManager.trackScreen("SearchScreen")
intent {
- val all = currencyRepo.getCurrencyNameUnsafe()
+ val all = currencyRepo.getCurrencyNameUnsafe().mapToSearchModel()
val frequent =
calcFrequentCurrUseCase.invoke()
.map { currencyRepo.nameByCodeUnsafe(it) }
- val topResults = getTopResultUseCase()
+ .mapToSearchModel()
+ val topResults = getTopResultUseCase().mapToSearchModel()
reduce {
state.copy(
@@ -78,12 +88,26 @@ class SearchViewModel(
reduce { state.copy(filter = input, topResultsFiltered = filtered) }
}
- fun onClick(name: CurrencyName) =
+ fun onClick(model: CurrencySearchModel) =
intent {
- emitResult(name)
+ prohibitedCodes?.let {
+ if (model.code in prohibitedCodes) {
+ reduce {
+ state.copy(showCodeProhibitedDialog = true)
+ }
+ return@intent
+ }
+ }
+
+ emitResult(CurrencyName(code = model.code, name = model.name))
postSideEffect(SearchScreenEffect.NavigateBack)
}
+ fun onCodeProhibitedDialogDismiss() =
+ intent {
+ reduce { state.copy(showCodeProhibitedDialog = false) }
+ }
+
private suspend fun emitResult(name: CurrencyName) {
val appFlowKey = AppSharedFlowKey.valueOf(appSharedFlowKeyString)
when (appFlowKey) {
@@ -108,11 +132,18 @@ class SearchViewModel(
AppSharedFlow.AddQuickCode.flow.emit(name.code)
}
}
+
+ private fun List.mapToSearchModel() =
+ map { name ->
+ val isProhibited = prohibitedCodes?.let { name.code in it } ?: false
+ CurrencySearchModel(code = name.code, name = name.name, isProhibited = isProhibited)
+ }
}
class SearchViewModelFactory @AssistedInject constructor(
@Assisted private val appSharedFlowKeyString: String,
@Assisted private val pos: Int?,
+ @Assisted private val prohibitedCodes: List?,
private val currencyRepo: CurrencyRepo,
private val calcFrequentCurrUseCase: CalcFrequentCurrUseCase,
private val getTopResultUseCase: GetTopResultUseCase,
@@ -122,6 +153,7 @@ class SearchViewModelFactory @AssistedInject constructor(
return SearchViewModel(
appSharedFlowKeyString,
pos,
+ prohibitedCodes,
currencyRepo,
calcFrequentCurrUseCase,
getTopResultUseCase,
@@ -135,6 +167,7 @@ class SearchViewModelFactory @AssistedInject constructor(
fun create(
appSharedFlowKeyString: String,
pos: Int?,
+ prohibitedCodes: List?,
): SearchViewModelFactory
}
}