From b73d7e9bada5cecac2bce665880df39c314751ca Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sat, 21 Dec 2024 07:10:34 +0600 Subject: [PATCH 1/2] Quick: swap inpunt/output btn --- .../arkbuilders/rate/core/domain/CurrUtils.kt | 1 - .../src/main/res/drawable/ic_refresh.xml | 12 +++++++ .../quick/presentation/add/AddQuickScreen.kt | 32 +++++++++++++++++-- .../presentation/add/AddQuickViewModel.kt | 26 +++++++++++++++ 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 core/presentation/src/main/res/drawable/ic_refresh.xml diff --git a/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt index ec0bd4d11..2adf09b63 100644 --- a/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt +++ b/core/domain/src/main/java/dev/arkbuilders/rate/core/domain/CurrUtils.kt @@ -64,7 +64,6 @@ object CurrUtils { "#." + "#".repeat(fractionSize), DecimalFormatSymbols(Locale.ENGLISH), ) - df.roundingMode = RoundingMode.CEILING return df.format(number) } diff --git a/core/presentation/src/main/res/drawable/ic_refresh.xml b/core/presentation/src/main/res/drawable/ic_refresh.xml new file mode 100644 index 000000000..ad5cec5cb --- /dev/null +++ b/core/presentation/src/main/res/drawable/ic_refresh.xml @@ -0,0 +1,12 @@ + + + + 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..2fa235f13 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 @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -25,6 +26,7 @@ import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -60,7 +62,7 @@ import dev.arkbuilders.rate.core.presentation.AppSharedFlowKey import dev.arkbuilders.rate.core.presentation.R import dev.arkbuilders.rate.core.presentation.theme.ArkColor import dev.arkbuilders.rate.core.presentation.ui.AppButton -import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv16 +import dev.arkbuilders.rate.core.presentation.ui.AppHorDiv import dev.arkbuilders.rate.core.presentation.ui.AppTopBarBack import dev.arkbuilders.rate.core.presentation.ui.ArkBasicTextField import dev.arkbuilders.rate.core.presentation.ui.DropDownWithIcon @@ -150,6 +152,7 @@ fun AddQuickScreen( ), ) }, + onSwapClick = viewModel::onSwapClick, onAddAsset = viewModel::onAddQuickPair, ) } @@ -165,6 +168,7 @@ private fun Content( onCurrencyRemove: (Int) -> Unit = {}, onGroupSelect: (String) -> Unit = {}, onCodeChange: (Int) -> Unit = {}, + onSwapClick: () -> Unit = {}, onAddAsset: () -> Unit = {}, ) { var showNewGroupDialog by remember { mutableStateOf(false) } @@ -184,7 +188,7 @@ private fun Content( .weight(1f) .verticalScroll(rememberScrollState()), ) { - Currencies(state, onAmountChanged, onCurrencyRemove, onCodeChange) + Currencies(state, onAmountChanged, onCurrencyRemove, onCodeChange, onSwapClick) Button( modifier = Modifier.padding(start = 16.dp, top = 16.dp), shape = RoundedCornerShape(8.dp), @@ -277,6 +281,7 @@ private fun Currencies( onAmountChanged: (String) -> Unit, onCurrencyRemove: (Int) -> Unit, onCodeChange: (Int) -> Unit, + onSwapClick: () -> Unit, ) { val from = state.currencies.first() Text( @@ -292,7 +297,7 @@ private fun Currencies( onAmountChanged = onAmountChanged, onCodeChange = onCodeChange, ) - AppHorDiv16(modifier = Modifier.padding(top = 16.dp)) + SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick) Text( modifier = Modifier.padding(top = 16.dp, start = 16.dp), text = "To", @@ -468,3 +473,24 @@ private fun ToResult( } } } + +@Composable +private fun SwapBtn( + modifier: Modifier = Modifier, + onClick: () -> Unit, +) { + Row(modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { + AppHorDiv(modifier = Modifier.weight(1f).padding(start = 16.dp, end = 12.dp)) + OutlinedButton( + modifier = Modifier.size(40.dp), + shape = CircleShape, + border = BorderStroke(1.dp, ArkColor.BorderSecondary), + contentPadding = PaddingValues(0.dp), + onClick = onClick, + colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.Black), + ) { + Icon(painter = painterResource(R.drawable.ic_refresh), contentDescription = null) + } + AppHorDiv(modifier = Modifier.weight(1f).padding(start = 12.dp, end = 16.dp)) + } +} 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..032bfb9e6 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 @@ -138,6 +138,32 @@ class AddQuickViewModel( checkFinishEnabled() } + fun onSwapClick() = + intent { + if (state.currencies.size < 2) + return@intent + + val newFrom = + state.currencies.last().let { amount -> + if (amount.value.isEmpty()) + return@let amount + + val withoutCommas = amount.value.replace(",", "") + amount.copy(value = CurrUtils.roundOff(withoutCommas.toBigDecimalArk())) + } + val newList = + state.currencies.toMutableList().apply { + removeLast() + add(0, newFrom) + } + + val calc = calcToResult(newList) + + reduce { + state.copy(currencies = calc) + } + } + fun onAddQuickPair() = intent { val from = state.currencies.first() From f070f8231951abe24bdfc2728383c0b272a42473 Mon Sep 17 00:00:00 2001 From: mdrlzy Date: Sun, 22 Dec 2024 06:58:24 +0600 Subject: [PATCH 2/2] Switch input/output with drag --- .../rate/core/presentation/theme/ArkColor.kt | 2 + .../rate/core/presentation/utils/Haptic.kt | 53 +++ .../src/main/res/drawable/ic_drag.xml | 30 ++ feature/quick/build.gradle.kts | 1 + .../quick/presentation/add/AddQuickScreen.kt | 339 ++++++++++++------ .../presentation/add/AddQuickViewModel.kt | 13 + gradle/libs.versions.toml | 2 + 7 files changed, 332 insertions(+), 108 deletions(-) create mode 100644 core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/Haptic.kt create mode 100644 core/presentation/src/main/res/drawable/ic_drag.xml diff --git a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt index 8d3cc6438..760b68402 100644 --- a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/theme/ArkColor.kt @@ -40,4 +40,6 @@ object ArkColor { val UtilitySuccess200 = Color(0xFFABEFC6) val UtilitySuccess500 = Color(0xFF17B26A) val UtilitySuccess700 = Color(0xFF067647) + + val NeutralGray500 = Color(0xFF6C737F) } diff --git a/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/Haptic.kt b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/Haptic.kt new file mode 100644 index 000000000..d1fdadf54 --- /dev/null +++ b/core/presentation/src/main/java/dev/arkbuilders/rate/core/presentation/utils/Haptic.kt @@ -0,0 +1,53 @@ +package dev.arkbuilders.rate.core.presentation.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalView +import androidx.core.view.HapticFeedbackConstantsCompat +import androidx.core.view.ViewCompat + +enum class ReorderHapticFeedbackType { + START, + MOVE, + END, +} + +open class ReorderHapticFeedback { + open fun performHapticFeedback(type: ReorderHapticFeedbackType) { + // no-op + } +} + +@Composable +fun rememberReorderHapticFeedback(): ReorderHapticFeedback { + val view = LocalView.current + + val reorderHapticFeedback = + remember { + object : ReorderHapticFeedback() { + override fun performHapticFeedback(type: ReorderHapticFeedbackType) { + when (type) { + ReorderHapticFeedbackType.START -> + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.GESTURE_START, + ) + + ReorderHapticFeedbackType.MOVE -> + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.SEGMENT_FREQUENT_TICK, + ) + + ReorderHapticFeedbackType.END -> + ViewCompat.performHapticFeedback( + view, + HapticFeedbackConstantsCompat.GESTURE_END, + ) + } + } + } + } + + return reorderHapticFeedback +} diff --git a/core/presentation/src/main/res/drawable/ic_drag.xml b/core/presentation/src/main/res/drawable/ic_drag.xml new file mode 100644 index 000000000..443f26407 --- /dev/null +++ b/core/presentation/src/main/res/drawable/ic_drag.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/feature/quick/build.gradle.kts b/feature/quick/build.gradle.kts index 35017441b..b9ffb11c2 100644 --- a/feature/quick/build.gradle.kts +++ b/feature/quick/build.gradle.kts @@ -56,6 +56,7 @@ dependencies { implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) implementation(libs.constraintlayout.compose) + implementation(libs.reorderable) implementation(libs.timber) 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 2fa235f13..2f6b17b47 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 @@ -1,8 +1,10 @@ -@file:OptIn(ExperimentalMaterial3Api::class) +@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) package dev.arkbuilders.rate.feature.quick.presentation.add import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll @@ -16,11 +18,15 @@ 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.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ExperimentalMaterial3Api @@ -42,6 +48,7 @@ import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType @@ -69,10 +76,17 @@ import dev.arkbuilders.rate.core.presentation.ui.DropDownWithIcon import dev.arkbuilders.rate.core.presentation.ui.GroupCreateDialog import dev.arkbuilders.rate.core.presentation.ui.GroupSelectPopup import dev.arkbuilders.rate.core.presentation.ui.NotifyAddedSnackbarVisuals +import dev.arkbuilders.rate.core.presentation.utils.ReorderHapticFeedback +import dev.arkbuilders.rate.core.presentation.utils.ReorderHapticFeedbackType +import dev.arkbuilders.rate.core.presentation.utils.rememberReorderHapticFeedback import dev.arkbuilders.rate.feature.quick.di.QuickComponentHolder import dev.arkbuilders.rate.feature.search.presentation.destinations.SearchCurrencyScreenDestination import org.orbitmvi.orbit.compose.collectAsState import org.orbitmvi.orbit.compose.collectSideEffect +import sh.calvin.reorderable.ReorderableCollectionItemScope +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.ReorderableLazyListState +import sh.calvin.reorderable.rememberReorderableLazyListState @Composable @Destination @@ -153,6 +167,7 @@ fun AddQuickScreen( ) }, onSwapClick = viewModel::onSwapClick, + onPairsSwap = viewModel::onPairsSwap, onAddAsset = viewModel::onAddQuickPair, ) } @@ -169,6 +184,7 @@ private fun Content( onGroupSelect: (String) -> Unit = {}, onCodeChange: (Int) -> Unit = {}, onSwapClick: () -> Unit = {}, + onPairsSwap: (from: Int, to: Int) -> Unit = { _, _ -> }, onAddAsset: () -> Unit = {}, ) { var showNewGroupDialog by remember { mutableStateOf(false) } @@ -181,78 +197,98 @@ private fun Content( } } + val haptic = rememberReorderHapticFeedback() + val lazyListState = rememberLazyListState() + val reorderableLazyColumnState = + rememberReorderableLazyListState(lazyListState) { from, to -> + val fromIndex = state.currencies.indexOfFirst { it.code == from.key } + val toIndex = state.currencies.indexOfFirst { it.code == to.key } + onPairsSwap(fromIndex, toIndex) + haptic.performHapticFeedback(ReorderHapticFeedbackType.MOVE) + } + Column(modifier = Modifier.fillMaxSize()) { - Column( + LazyColumn( modifier = Modifier - .weight(1f) - .verticalScroll(rememberScrollState()), + .weight(1f), + state = lazyListState, ) { - Currencies(state, onAmountChanged, onCurrencyRemove, onCodeChange, onSwapClick) - Button( - modifier = Modifier.padding(start = 16.dp, top = 16.dp), - shape = RoundedCornerShape(8.dp), - border = BorderStroke(1.dp, ArkColor.Border), - colors = - ButtonDefaults.buttonColors( - containerColor = Color.Transparent, - contentColor = ArkColor.FGSecondary, - ), - onClick = { onNewCurrencyClick() }, - contentPadding = PaddingValues(0.dp), - ) { - Icon( - modifier = Modifier.padding(start = 20.dp), - painter = painterResource(id = R.drawable.ic_add), - contentDescription = "", - ) - Text( - modifier = - Modifier.padding( - start = 8.dp, - top = 10.dp, - bottom = 10.dp, - end = 18.dp, - ), - text = stringResource(R.string.new_currency), - fontWeight = FontWeight.SemiBold, - fontSize = 16.sp, - ) - } - DropDownWithIcon( - modifier = - Modifier - .fillMaxWidth() - .padding(top = 16.dp, start = 16.dp, end = 16.dp) - .onPlaced { - addGroupBtnWidth = it.size.width - }, - onClick = { showGroupsPopup = true }, - title = - state.group?.let { state.group } - ?: stringResource(R.string.add_group), - icon = painterResource(id = R.drawable.ic_group), + currencies( + state, + reorderableLazyColumnState, + haptic, + onAmountChanged, + onCurrencyRemove, + onCodeChange, + onSwapClick, ) - if (showGroupsPopup) { - Box( - modifier = - Modifier.padding( - start = 16.dp, - top = 4.dp, + item { + Button( + modifier = Modifier.padding(start = 16.dp, top = 16.dp), + shape = RoundedCornerShape(8.dp), + border = BorderStroke(1.dp, ArkColor.Border), + colors = + ButtonDefaults.buttonColors( + containerColor = Color.Transparent, + contentColor = ArkColor.FGSecondary, ), + onClick = { onNewCurrencyClick() }, + contentPadding = PaddingValues(0.dp), ) { - Popup( - offset = IntOffset(0, 0), - properties = PopupProperties(), - onDismissRequest = { showGroupsPopup = false }, + Icon( + modifier = Modifier.padding(start = 20.dp), + painter = painterResource(id = R.drawable.ic_add), + contentDescription = "", + ) + Text( + modifier = + Modifier.padding( + start = 8.dp, + top = 10.dp, + bottom = 10.dp, + end = 18.dp, + ), + text = stringResource(R.string.new_currency), + fontWeight = FontWeight.SemiBold, + fontSize = 16.sp, + ) + } + DropDownWithIcon( + modifier = + Modifier + .fillMaxWidth() + .padding(top = 16.dp, start = 16.dp, end = 16.dp) + .onPlaced { + addGroupBtnWidth = it.size.width + }, + onClick = { showGroupsPopup = true }, + title = + state.group?.let { state.group } + ?: stringResource(R.string.add_group), + icon = painterResource(id = R.drawable.ic_group), + ) + if (showGroupsPopup) { + Box( + modifier = + Modifier.padding( + start = 16.dp, + top = 4.dp, + ), ) { - GroupSelectPopup( - groups = state.availableGroups, - widthPx = addGroupBtnWidth, - onGroupSelect = { onGroupSelect(it) }, - onNewGroupClick = { showNewGroupDialog = true }, - onDismiss = { showGroupsPopup = false }, - ) + Popup( + offset = IntOffset(0, 0), + properties = PopupProperties(), + onDismissRequest = { showGroupsPopup = false }, + ) { + GroupSelectPopup( + groups = state.availableGroups, + widthPx = addGroupBtnWidth, + onGroupSelect = { onGroupSelect(it) }, + onNewGroupClick = { showNewGroupDialog = true }, + onDismiss = { showGroupsPopup = false }, + ) + } } } } @@ -275,62 +311,110 @@ private fun Content( } } -@Composable -private fun Currencies( +private fun LazyListScope.currencies( state: AddQuickScreenState, + reorderableLazyColumnState: ReorderableLazyListState, + haptic: ReorderHapticFeedback, onAmountChanged: (String) -> Unit, onCurrencyRemove: (Int) -> Unit, onCodeChange: (Int) -> Unit, onSwapClick: () -> Unit, ) { val from = state.currencies.first() - Text( - modifier = Modifier.padding(top = 16.dp, start = 16.dp), - text = "From", - fontWeight = FontWeight.Medium, - color = ArkColor.TextSecondary, - ) - FromInput( - index = 0, - code = from.code, - amount = from.value, - onAmountChanged = onAmountChanged, - onCodeChange = onCodeChange, - ) - SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick) - Text( - modifier = Modifier.padding(top = 16.dp, start = 16.dp), - text = "To", - fontWeight = FontWeight.Medium, - color = ArkColor.TextSecondary, - ) - state.currencies.forEachIndexed { index, amountStr -> - if (index == 0) - return@forEachIndexed + val to = state.currencies.drop(1) - ToResult( - index = index, - code = amountStr.code, - amount = amountStr.value, - onCurrencyRemove = onCurrencyRemove, - onCodeChange = onCodeChange, + item { + Text( + modifier = Modifier.padding(top = 16.dp, start = 52.dp), + text = "From", + fontWeight = FontWeight.Medium, + color = ArkColor.TextSecondary, ) } + item(key = from.code) { + ReorderableItem(state = reorderableLazyColumnState, key = from.code) { + FromInput( + code = from.code, + amount = from.value, + haptic = haptic, + scope = this, + onAmountChanged = onAmountChanged, + onCodeChange = { + val index = state.currencies.indexOfFirst { it.code == from.code } + onCodeChange(index) + }, + ) + } + } + item { + SwapBtn(modifier = Modifier.padding(top = 16.dp), onClick = onSwapClick) + Text( + modifier = Modifier.padding(top = 16.dp, start = 52.dp), + text = "To", + fontWeight = FontWeight.Medium, + color = ArkColor.TextSecondary, + ) + } + itemsIndexed(to, key = { _, amount -> amount.code }) { index, item -> + ReorderableItem(state = reorderableLazyColumnState, key = item.code) { + ToResult( + code = item.code, + amount = item.value, + scope = this, + haptic = haptic, + onCurrencyRemove = { + val index = state.currencies.indexOfFirst { it.code == item.code } + onCurrencyRemove(index) + }, + onCodeChange = { + val index = state.currencies.indexOfFirst { it.code == item.code } + onCodeChange(index) + }, + ) + } + } } @Composable private fun FromInput( - index: Int, code: CurrencyCode, amount: String, + haptic: ReorderHapticFeedback, + scope: ReorderableCollectionItemScope, onAmountChanged: (String) -> Unit, - onCodeChange: (Int) -> Unit, + onCodeChange: () -> Unit, ) { Row(modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp)) { + Box( + modifier = + with(scope) { + Modifier + .width(24.dp) + .height(44.dp) + .draggableHandle( + onDragStarted = { + haptic.performHapticFeedback(ReorderHapticFeedbackType.START) + }, + onDragStopped = { + haptic.performHapticFeedback(ReorderHapticFeedbackType.END) + }, + ) + .clearAndSetSemantics { } + }, + ) { + Icon( + modifier = Modifier.align(Alignment.Center), + painter = painterResource(R.drawable.ic_drag), + contentDescription = null, + tint = ArkColor.NeutralGray500, + ) + } + Row( modifier = Modifier .weight(1f) + .padding(start = 12.dp) .height(44.dp) .border( 1.dp, @@ -345,7 +429,7 @@ private fun FromInput( Modifier .fillMaxHeight() .clip(RoundedCornerShape(8.dp)) - .clickable { onCodeChange(index) }, + .clickable { onCodeChange() }, verticalAlignment = Alignment.CenterVertically, ) { Text( @@ -390,24 +474,52 @@ private fun FromInput( @Composable private fun ToResult( - index: Int, code: CurrencyCode, amount: String, - onCurrencyRemove: (Int) -> Unit, - onCodeChange: (Int) -> Unit, + haptic: ReorderHapticFeedback, + scope: ReorderableCollectionItemScope, + onCurrencyRemove: () -> Unit, + onCodeChange: () -> Unit, ) { Row(modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp)) { + Box( + modifier = + with(scope) { + Modifier + .width(24.dp) + .height(44.dp) + .draggableHandle( + onDragStarted = { + haptic.performHapticFeedback(ReorderHapticFeedbackType.START) + }, + onDragStopped = { + haptic.performHapticFeedback(ReorderHapticFeedbackType.END) + }, + ) + .clearAndSetSemantics { } + }, + ) { + Icon( + modifier = Modifier.align(Alignment.Center), + painter = painterResource(R.drawable.ic_drag), + contentDescription = null, + tint = ArkColor.NeutralGray500, + ) + } + Row( modifier = Modifier .weight(1f) + .padding(start = 12.dp) .height(44.dp) .border( 1.dp, ArkColor.Border, RoundedCornerShape(8.dp), ) - .clip(RoundedCornerShape(8.dp)), + .clip(RoundedCornerShape(8.dp)) + .background(Color.White), verticalAlignment = Alignment.CenterVertically, ) { Row( @@ -415,7 +527,7 @@ private fun ToResult( Modifier .fillMaxHeight() .clip(RoundedCornerShape(8.dp)) - .clickable { onCodeChange(index) }, + .clickable { onCodeChange() }, verticalAlignment = Alignment.CenterVertically, ) { Text( @@ -462,7 +574,8 @@ private fun ToResult( RoundedCornerShape(8.dp), ) .clip(RoundedCornerShape(8.dp)) - .clickable { onCurrencyRemove(index) }, + .background(Color.White) + .clickable { onCurrencyRemove() }, contentAlignment = Alignment.Center, ) { Icon( @@ -480,7 +593,12 @@ private fun SwapBtn( onClick: () -> Unit, ) { Row(modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) { - AppHorDiv(modifier = Modifier.weight(1f).padding(start = 16.dp, end = 12.dp)) + AppHorDiv( + modifier = + Modifier + .weight(1f) + .padding(start = 16.dp, end = 12.dp), + ) OutlinedButton( modifier = Modifier.size(40.dp), shape = CircleShape, @@ -491,6 +609,11 @@ private fun SwapBtn( ) { Icon(painter = painterResource(R.drawable.ic_refresh), contentDescription = null) } - AppHorDiv(modifier = Modifier.weight(1f).padding(start = 12.dp, end = 16.dp)) + AppHorDiv( + modifier = + Modifier + .weight(1f) + .padding(start = 12.dp, end = 16.dp), + ) } } 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 032bfb9e6..bae410aeb 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 @@ -164,6 +164,19 @@ class AddQuickViewModel( } } + fun onPairsSwap( + from: Int, + to: Int, + ) = intent { + val new = + state.currencies.toMutableList().apply { + add(to, removeAt(from)) + } + reduce { + state.copy(currencies = new) + } + } + fun onAddQuickPair() = intent { val from = state.currencies.first() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d936546e6..c8fc45036 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,7 @@ workRuntimeKtx = "2.8.1" appcompat = "1.7.0" material = "1.12.0" gson = "2.11.0" +reorderable = "2.4.2" [libraries] ark-about = { module = "dev.arkbuilders.components:about", version.ref = "arkAbout" } @@ -85,6 +86,7 @@ timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } +reorderable = { group = "sh.calvin.reorderable", name = "reorderable", version.ref = "reorderable" } [plugins] android-library = { id = "com.android.library", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }