From 0c0bf8cb341e5e7d770c71b6ca2aa5f43a700f6c Mon Sep 17 00:00:00 2001 From: Honza Rychnovsky Date: Mon, 20 Mar 2023 10:17:22 +0100 Subject: [PATCH] [#785] Remove press-and-hold from Send * [#785] Remove press-and-hold for send confirmation - Timed button replaced by standard behaviour button - Related SendView test updated * Remove unused API If we need this back, we can restore it from the Git history. * Remove unused imports --------- Co-authored-by: Carter Jernigan --- .../zcash/ui/design/component/ButtonTest.kt | 93 ------------------- .../zcash/ui/design/component/Button.kt | 51 ---------- .../zcash/ui/screen/send/view/SendViewTest.kt | 22 ++--- .../zcash/ui/screen/send/view/SendView.kt | 20 +--- .../src/main/res/ui/send/values/strings.xml | 2 +- 5 files changed, 11 insertions(+), 177 deletions(-) delete mode 100644 ui-design-lib/src/androidTest/java/co/electriccoin/zcash/ui/design/component/ButtonTest.kt diff --git a/ui-design-lib/src/androidTest/java/co/electriccoin/zcash/ui/design/component/ButtonTest.kt b/ui-design-lib/src/androidTest/java/co/electriccoin/zcash/ui/design/component/ButtonTest.kt deleted file mode 100644 index 1b426eeb..00000000 --- a/ui-design-lib/src/androidTest/java/co/electriccoin/zcash/ui/design/component/ButtonTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -package co.electriccoin.zcash.ui.design.component - -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.material3.Text -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.test.junit4.ComposeContentTestRule -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performClick -import androidx.test.filters.MediumTest -import co.electriccoin.zcash.ui.design.theme.ZcashTheme -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.advanceTimeBy -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.testTimeSource -import org.junit.Rule -import org.junit.Test -import kotlin.test.assertFalse -import kotlin.test.assertTrue -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds -import kotlin.time.ExperimentalTime - -class ButtonTest { - @get:Rule - val composeTestRule = createComposeRule() - - @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTime::class) - @Test - @MediumTest - fun timedButtonTest(): Unit = runTest { - val testDispatcher = StandardTestDispatcher(testScheduler) - val testSetup = newTestSetup(testDispatcher, 2.seconds) - val mark = testTimeSource.markNow() - - launch(Dispatchers.Main) { - testSetup.interactionSource.emit(PressInteraction.Press(Offset.Zero)) - advanceTimeBy(3.seconds.inWholeMilliseconds) - } - - launch { - testSetup.mutableActionExecuted.collect { - if (!it) return@collect - - assertTrue { mark.elapsedNow() >= 2.seconds } - this.cancel() - } - } - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - @MediumTest - fun buttonClickTest() = runTest { - val testDispatcher = StandardTestDispatcher(testScheduler) - val testSetup = newTestSetup(testDispatcher, 2.seconds) - - composeTestRule.onNodeWithText("button").also { - it.performClick() - } - advanceTimeBy(3.seconds.inWholeMilliseconds) - assertFalse { testSetup.mutableActionExecuted.value } - } - - private fun newTestSetup(testDispatcher: CoroutineDispatcher, duration: Duration) = TestSetup(testDispatcher, composeTestRule, duration) - - private class TestSetup(coroutineDispatcher: CoroutineDispatcher, composeTestRule: ComposeContentTestRule, duration: Duration) { - val mutableActionExecuted = MutableStateFlow(false) - val interactionSource = MutableInteractionSource() - - init { - composeTestRule.setContent { - ZcashTheme { - TimedButton( - duration = duration, - onClick = { mutableActionExecuted.update { true } }, - coroutineDispatcher = coroutineDispatcher, - content = { Text(text = "button") }, - interactionSource = interactionSource - ) - } - } - } - } -} diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt index 09934d77..0ea9aee1 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Button.kt @@ -1,9 +1,6 @@ package co.electriccoin.zcash.ui.design.component -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button @@ -12,20 +9,10 @@ import androidx.compose.material3.ButtonDefaults.buttonColors import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import co.electriccoin.zcash.ui.design.theme.ZcashTheme -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import kotlin.time.Duration -import kotlin.time.Duration.Companion.seconds @Preview @Composable @@ -158,41 +145,3 @@ fun DangerousButton( ) } } - -@Suppress("LongParameterList") -@Composable -fun TimedButton( - onClick: () -> Unit, - content: @Composable (RowScope.() -> Unit), - modifier: Modifier = Modifier, - duration: Duration = 5.seconds, - interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - coroutineDispatcher: CoroutineDispatcher = Dispatchers.Default -) { - LaunchedEffect(interactionSource) { - var action: Job? = null - - interactionSource.interactions.collect { interaction -> - when (interaction) { - is PressInteraction.Press -> { - action = launch(coroutineDispatcher) { - delay(duration) - withContext(Dispatchers.Main) { - onClick() - } - } - } - is PressInteraction.Release -> { - action?.cancel() - } - } - } - } - - Button( - modifier = modifier, - onClick = {}, - interactionSource = interactionSource, - content = content - ) -} diff --git a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt index 8ecb88b6..f2e3e3a6 100644 --- a/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt +++ b/ui-lib/src/androidTest/java/co/electriccoin/zcash/ui/screen/send/view/SendViewTest.kt @@ -1,8 +1,5 @@ package co.electriccoin.zcash.ui.screen.send.view -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.interaction.PressInteraction -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.junit4.ComposeContentTestRule @@ -26,13 +23,11 @@ import co.electriccoin.zcash.test.UiTestPrerequisites import co.electriccoin.zcash.ui.R import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.test.getStringResource -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -70,7 +65,7 @@ class SendViewTest : UiTestPrerequisites() { composeTestRule.setValidAddress() composeTestRule.clickCreateAndSend() composeTestRule.assertOnConfirmation() - clickConfirmation(testSetup.interactionSource) + composeTestRule.clickConfirmation() launch { testSetup.mutableActionExecuted.collectWith(this) { @@ -106,7 +101,7 @@ class SendViewTest : UiTestPrerequisites() { composeTestRule.clickCreateAndSend() composeTestRule.assertOnConfirmation() - clickConfirmation(testSetup.interactionSource) + composeTestRule.clickConfirmation() launch { testSetup.mutableActionExecuted.collectWith(this) { @@ -164,7 +159,7 @@ class SendViewTest : UiTestPrerequisites() { composeTestRule.clickCreateAndSend() composeTestRule.assertOnConfirmation() - clickConfirmation(testSetup.interactionSource) + composeTestRule.clickConfirmation() launch { testSetup.mutableActionExecuted.collectWith(this) { @@ -242,7 +237,7 @@ class SendViewTest : UiTestPrerequisites() { composeTestRule.clickCreateAndSend() composeTestRule.assertOnConfirmation() - clickConfirmation(testSetup.interactionSource) + composeTestRule.clickConfirmation() launch { testSetup.mutableActionExecuted.collectWith(this) { @@ -296,7 +291,6 @@ class SendViewTest : UiTestPrerequisites() { private val onBackCount = AtomicInteger(0) private val onCreateCount = AtomicInteger(0) - val interactionSource = MutableInteractionSource() val mutableActionExecuted = MutableStateFlow(false) @Volatile @@ -322,7 +316,6 @@ class SendViewTest : UiTestPrerequisites() { ZcashTheme { Send( mySpendableBalance = ZatoshiFixture.new(), - pressAndHoldInteractionSource = interactionSource, goBack = { onBackCount.incrementAndGet() }, @@ -386,10 +379,9 @@ private fun ComposeContentTestRule.clickCreateAndSend() { } } -@OptIn(ExperimentalCoroutinesApi::class) -private fun TestScope.clickConfirmation(interactionSource: MutableInteractionSource) { - launch(Dispatchers.Main) { - interactionSource.emit(PressInteraction.Press(Offset.Zero)) +private fun ComposeContentTestRule.clickConfirmation() { + onNodeWithText(getStringResource(R.string.send_confirm)).also { + it.performClick() } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt index 6b22e10c..d834dfa7 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/send/view/SendView.kt @@ -1,6 +1,5 @@ package co.electriccoin.zcash.ui.screen.send.view -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight @@ -22,7 +21,6 @@ import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier @@ -47,7 +45,6 @@ import co.electriccoin.zcash.ui.design.component.FormTextField import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.Header import co.electriccoin.zcash.ui.design.component.PrimaryButton -import co.electriccoin.zcash.ui.design.component.TimedButton import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.ZcashTheme.dimens import co.electriccoin.zcash.ui.screen.send.ext.ABBREVIATION_INDEX @@ -69,16 +66,12 @@ fun PreviewSend() { } } -/** - * @param pressAndHoldInteractionSource This is an argument that can be injected for automated testing. - */ @OptIn(ExperimentalMaterial3Api::class) @Composable fun Send( mySpendableBalance: Zatoshi, goBack: () -> Unit, - onCreateAndSend: (ZecSend) -> Unit, - pressAndHoldInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() } + onCreateAndSend: (ZecSend) -> Unit ) { // For now, we're avoiding sub-navigation to keep the navigation logic simple. But this might // change once deep-linking support is added. It depends on whether deep linking should do one of: @@ -98,7 +91,6 @@ fun Send( SendMainContent( myBalance = mySpendableBalance, sendStage = sendStage, - pressAndHoldInteractionSource = pressAndHoldInteractionSource, setSendStage = setSendStage, onCreateAndSend = onCreateAndSend, modifier = Modifier @@ -138,7 +130,6 @@ private fun SendTopAppBar(onBack: () -> Unit) { private fun SendMainContent( myBalance: Zatoshi, sendStage: SendStage, - pressAndHoldInteractionSource: MutableInteractionSource, setSendStage: (SendStage) -> Unit, onCreateAndSend: (ZecSend) -> Unit, modifier: Modifier = Modifier @@ -158,7 +149,6 @@ private fun SendMainContent( } else { Confirmation( zecSend = zecSend, - pressAndHoldInteractionSource = pressAndHoldInteractionSource, onConfirmation = { onCreateAndSend(zecSend) }, @@ -293,7 +283,6 @@ private fun SendForm( @Composable private fun Confirmation( zecSend: ZecSend, - pressAndHoldInteractionSource: MutableInteractionSource, onConfirmation: () -> Unit, modifier: Modifier = Modifier ) { @@ -306,12 +295,9 @@ private fun Confirmation( ) ) - TimedButton( + PrimaryButton( onClick = onConfirmation, - { - Text(text = stringResource(id = R.string.send_confirm)) - }, - interactionSource = pressAndHoldInteractionSource + text = stringResource(id = R.string.send_confirm) ) } } diff --git a/ui-lib/src/main/res/ui/send/values/strings.xml b/ui-lib/src/main/res/ui/send/values/strings.xml index e61e7c8e..4d198d33 100644 --- a/ui-lib/src/main/res/ui/send/values/strings.xml +++ b/ui-lib/src/main/res/ui/send/values/strings.xml @@ -12,6 +12,6 @@ Send %1$s ZEC to %2$s? %1$s%2$s - Press and hold to send ZEC + Press to send ZEC