From 643c0979a7916aafdb926246d8fbcafb4e8a68c5 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:40:10 -0500 Subject: [PATCH 01/73] remove example client test --- .../commonTest/kotlin/ExampleClientTest.kt | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 feature/ride/src/commonTest/kotlin/ExampleClientTest.kt diff --git a/feature/ride/src/commonTest/kotlin/ExampleClientTest.kt b/feature/ride/src/commonTest/kotlin/ExampleClientTest.kt deleted file mode 100644 index e4d7598..0000000 --- a/feature/ride/src/commonTest/kotlin/ExampleClientTest.kt +++ /dev/null @@ -1,23 +0,0 @@ -import kotlin.test.Test - -/* - * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. - */ - -/** - * - */ -class ExampleClientTest { - @Test - fun `connect to local server`() { - /* - Given: - - A local server is running - - Valid login credentials - When: - - The client sends a login request to the /auth endpoint - Then: - - The client receives a 200 OK response - */ - } -} From 34c09ee09740b9bb06578e2380eea4dc81c41955 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:40:14 -0500 Subject: [PATCH 02/73] remove module info --- feature/ride/src/commonMain/kotlin/ModuleInfo.kt | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 feature/ride/src/commonMain/kotlin/ModuleInfo.kt diff --git a/feature/ride/src/commonMain/kotlin/ModuleInfo.kt b/feature/ride/src/commonMain/kotlin/ModuleInfo.kt deleted file mode 100644 index 5c6b9e0..0000000 --- a/feature/ride/src/commonMain/kotlin/ModuleInfo.kt +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. - */ - -package org.pointyware.xyz.feature.ride - -const val MODULE_NAME = ":feature:ride" From 9483a2c5311a84c0c8dc293ed57d0f24aa33e1d4 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:42:48 -0500 Subject: [PATCH 03/73] Create :feature:ride test koin extensions --- .../test/KoinExt.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt new file mode 100644 index 0000000..1a4a598 --- /dev/null +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.test + +import org.koin.core.context.startKoin +import org.pointyware.xyz.core.data.di.coreDataModule +import org.pointyware.xyz.core.entities.di.coreEntitiesModule +import org.pointyware.xyz.core.interactors.di.coreInteractorsModule +import org.pointyware.xyz.core.navigation.di.coreNavigationModule +import org.pointyware.xyz.core.ui.di.coreUiModule +import org.pointyware.xyz.core.viewmodels.di.coreViewModelsModule +import org.pointyware.xyz.feature.ride.di.featureRideModule + + +/** + * + */ +fun setupKoin() { + startKoin { + modules( + coreUiModule(), + coreViewModelsModule(), + coreInteractorsModule(), + coreDataModule(), + coreEntitiesModule(), + coreNavigationModule(), + + featureRideModule(), + ) + } +} From c0bd4a747faf2f05407dc7d7ab09a28c70bde1c8 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:44:01 -0500 Subject: [PATCH 04/73] Add setupKoin docs --- .../kotlin/org.pointyware.xyz.feature.drive/test/KoinExt.kt | 2 +- .../kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/test/KoinExt.kt b/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/test/KoinExt.kt index 6a8c33d..fb96158 100644 --- a/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/test/KoinExt.kt +++ b/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/test/KoinExt.kt @@ -14,7 +14,7 @@ import org.pointyware.xyz.core.viewmodels.di.coreViewModelsModule import org.pointyware.xyz.drive.di.featureDriveModule /** - * TODO: describe purpose/intent of KoinExt + * Starts koin with the required modules for testing */ fun setupKoin() { startKoin { diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt index 1a4a598..61c9faf 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/test/KoinExt.kt @@ -15,7 +15,7 @@ import org.pointyware.xyz.feature.ride.di.featureRideModule /** - * + * Starts koin with the required modules for testing */ fun setupKoin() { startKoin { From 1affa51ee74d71c66db4588084845a5b4ff3b365 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:47:28 -0500 Subject: [PATCH 05/73] remove unsupported unit tests --- .github/workflows/main-release-flow.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main-release-flow.yml b/.github/workflows/main-release-flow.yml index 813d48b..ed0488e 100644 --- a/.github/workflows/main-release-flow.yml +++ b/.github/workflows/main-release-flow.yml @@ -30,7 +30,6 @@ jobs: - name: "Build: Assemble and Test entire project" run: | ./gradlew :app-android:buildRelease - ./gradlew testReleaseUnitTest # - run: ./gradlew generate coverage report, upload test/coverage reports From 0022a9955e967ead0fcd0e29ce3714ab6cd6ad9a Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Sun, 22 Sep 2024 22:49:31 -0500 Subject: [PATCH 06/73] Move passenger test to :feature:ride module --- .../ui/PassengerDashboardScreenUiTest.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename app-shared/src/commonTest/kotlin/org.pointyware.xyz.shared/ride/RequestRideUiTest.kt => feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt (96%) diff --git a/app-shared/src/commonTest/kotlin/org.pointyware.xyz.shared/ride/RequestRideUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt similarity index 96% rename from app-shared/src/commonTest/kotlin/org.pointyware.xyz.shared/ride/RequestRideUiTest.kt rename to feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 699cd70..22aa6aa 100644 --- a/app-shared/src/commonTest/kotlin/org.pointyware.xyz.shared/ride/RequestRideUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -2,7 +2,7 @@ * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. */ -package org.pointyware.xyz.shared.ride +package org.pointyware.xyz.feature.ride.ui import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assert @@ -25,10 +25,9 @@ import org.pointyware.xyz.core.navigation.StackNavigationController import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule -import org.pointyware.xyz.feature.ride.ui.PassengerDashboardScreen +import org.pointyware.xyz.feature.ride.test.setupKoin import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel -import org.pointyware.xyz.shared.di.setupKoin import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -38,7 +37,7 @@ import kotlin.test.assertEquals * System/UI Test for Rider Request Ride View */ @OptIn(ExperimentalTestApi::class) -class RequestRideUiTest { +class PassengerDashboardScreenUiTest { @BeforeTest fun setUp() { From 4555104526d52bb11633662595bac990406387e7 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 09:55:51 -0500 Subject: [PATCH 07/73] Specify annotation targets --- .../org.pointyware.xyz.core.common/ParameterConstraints.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt b/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt index 28cff35..a62e56b 100644 --- a/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt +++ b/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt @@ -7,14 +7,17 @@ package org.pointyware.xyz.core.common /** * Expresses a constraint on the length of a string. */ +@Target(AnnotationTarget.PROPERTY) annotation class StringLength(val min: Int, val max: Int) /** * Expresses a constraint on the range of an integer. */ +@Target(AnnotationTarget.PROPERTY) annotation class IntRange(val min: Int, val max: Int) /** * Expresses a regular expression constraint on a string. */ +@Target(AnnotationTarget.PROPERTY) annotation class Regex(val pattern: String) From aa412d2a27d371a4ae7276e732a7c975486f24f3 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 09:58:29 -0500 Subject: [PATCH 08/73] Add annotation defaults --- .../org.pointyware.xyz.core.common/ParameterConstraints.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt b/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt index a62e56b..27f80b1 100644 --- a/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt +++ b/core/common/src/commonMain/kotlin/org.pointyware.xyz.core.common/ParameterConstraints.kt @@ -8,16 +8,16 @@ package org.pointyware.xyz.core.common * Expresses a constraint on the length of a string. */ @Target(AnnotationTarget.PROPERTY) -annotation class StringLength(val min: Int, val max: Int) +annotation class StringLength(val min: Int = 0, val max: Int = Int.MAX_VALUE) /** * Expresses a constraint on the range of an integer. */ @Target(AnnotationTarget.PROPERTY) -annotation class IntRange(val min: Int, val max: Int) +annotation class IntRange(val min: Int = Int.MIN_VALUE, val max: Int = Int.MAX_VALUE) /** * Expresses a regular expression constraint on a string. */ @Target(AnnotationTarget.PROPERTY) -annotation class Regex(val pattern: String) +annotation class Regex(val pattern: String = "^.*$") From 4b5ba8873b08ad37eb2c9fccfa53944259b7acb5 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:02:55 -0500 Subject: [PATCH 09/73] Create expiration date entity --- .../entities/ExpirationDate.kt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt new file mode 100644 index 0000000..63a3609 --- /dev/null +++ b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.entities + +import org.pointyware.xyz.core.common.IntRange + +/** + * A payment expiration date, composed of a month and year. + */ +data class ExpirationDate( + /** + * + */ + @IntRange(min = 1, max = 12) + val month: Byte, + /** + * + */ + @IntRange(min = 2024) + val year: Short +) { + init { + require(month in 1..12) { "Month must be between 1 and 12" } + require(year >= 2024) { "Year must be 2024 or later" } + } + + /** + * Formats the expiration date as a string in the format MM/YY. + */ + fun format(): String { + val monthStr = month.toString().padStart(2, '0') + val lastTwo = year % 100 + val yearStr = lastTwo.toString().padStart(2, '0') + return "$monthStr/$yearStr" + } +} From 009ced60fe14839e48b833cf60088fc4cde1f565 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:14:22 -0500 Subject: [PATCH 10/73] Use constants to express range expectations --- .../entities/ExpirationDate.kt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt index 63a3609..1da3646 100644 --- a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt +++ b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt @@ -6,24 +6,30 @@ package org.pointyware.xyz.feature.ride.entities import org.pointyware.xyz.core.common.IntRange +private const val MONTH_MIN = 1 +private const val MONTH_MAX = 12 +private const val YEAR_MIN = 2024 +private const val YEAR_MAX = 3023 + /** * A payment expiration date, composed of a month and year. */ data class ExpirationDate( /** - * + * The 1-indexed month of the expiration date. */ - @IntRange(min = 1, max = 12) + @IntRange(min = MONTH_MIN, max = MONTH_MAX) val month: Byte, + /** - * + * The year of the expiration date. */ - @IntRange(min = 2024) + @IntRange(min = YEAR_MIN, max = YEAR_MAX) val year: Short ) { init { - require(month in 1..12) { "Month must be between 1 and 12" } - require(year >= 2024) { "Year must be 2024 or later" } + require(month in MONTH_MIN..MONTH_MAX) { "Month must be between 1 and 12" } + require(year in YEAR_MIN .. YEAR_MAX) { "Year must be between 2024 and 3023" } } /** From 6c7b1e6e3464f3365ef45775047395c41f3e193c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:15:59 -0500 Subject: [PATCH 11/73] Add ExpirationDateTest --- .../entities/ExpirationDateTest.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDateTest.kt diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDateTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDateTest.kt new file mode 100644 index 0000000..e38c8d9 --- /dev/null +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDateTest.kt @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.entities + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFails + +/** + * + */ +class ExpirationDateTest { + + data class FormatCase( + val month: Byte, + val year: Short, + val expected: String + ) + + @Test + fun `format expiration date`() { + /* + Given: + - A month and year + When: + - Formatting the expiration date + */ + listOf( + FormatCase(1, 2024, "01/24"), + FormatCase(12, 2024, "12/24"), + FormatCase(2, 2025, "02/25"), + FormatCase(10, 2025, "10/25"), + ).forEach { (month, year, expected) -> + val expirationDate = ExpirationDate(month, year) + val formatted = expirationDate.format() + /* + Then: + - The expiration date should be formatted as MM/YY + */ + assertEquals(expected, formatted, "Expected $expected but got $formatted for $expirationDate") + } + } + + data class ConstructionCase( + val month: Byte, + val year: Short, + ) + + @Test + fun `invalid expiration month`() { + /* + Given: + - An invalid month + When: + - Creating an expiration date + Then: + - An exception should be thrown + */ + listOf( + ConstructionCase(-128, 2024), + ConstructionCase(0, 2024), + ConstructionCase(13, 2024), + ConstructionCase(127, 2024), + ).forEach { (month, year) -> + assertFails("Expected failure for month number $month") { + ExpirationDate(month, year) + } + } + } + + @Test + fun `invalid expiration year`() { + /* + Given: + - An invalid year + When: + - Creating an expiration date + Then: + - An exception should be thrown + */ + listOf( + ConstructionCase(4, 2020), + ConstructionCase(3, 2021), + ConstructionCase(2, 2022), + ConstructionCase(1, 2023), + ConstructionCase(5, 3024), + ConstructionCase(6, 3025), + ConstructionCase(7, 3026), + ConstructionCase(8, 3027), + ).forEach { (month, year) -> + assertFails("Expected failure for year $year") { + ExpirationDate(month, year) + } + } + } +} From aa26d7826336ffd45a760837d1c1c2bb38688374 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:21:13 -0500 Subject: [PATCH 12/73] Add payment selection to ride ui test --- .../ui/PassengerDashboardScreenUiTest.kt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 22aa6aa..b9c584b 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -92,6 +92,7 @@ class PassengerDashboardScreenUiTest { Then: - The "New Ride" button transforms into the Search Bar - The "Confirm" search button is shown but disabled + - The payment selection is shown */ onNodeWithText("New Ride") .performClick() @@ -101,6 +102,36 @@ class PassengerDashboardScreenUiTest { onNodeWithText("Confirm") .assertExists() .assertIsNotEnabled() + onNodeWithContentDescription("Payment Selection") + .assertExists() + + /* + When: + - User clicks on the "Payment Selection" button + Then: + - The "Payment Selection" button transforms into the Payment Method Selection + */ + + onNodeWithContentDescription("Payment Selection") + .performClick() + + onNodeWithContentDescription("Payment Method Selection") + .assertExists() + + /* + When: + - User selects a payment method - Bisa + Then: + - The "Payment Method Selection" button transforms back into the "Payment Selection" button + - The selected payment method is shown + */ + onNodeWithText("Bisa", substring = true) + .performClick() + + onNodeWithContentDescription("Payment Selection") + .assertExists() + onNodeWithText("Bisa", substring = true) + .assertExists() /* When: @@ -172,5 +203,31 @@ class PassengerDashboardScreenUiTest { onNodeWithText("Cancel Request") .assertExists() .assertIsEnabled() + + /* + When: + - A driver accepts the request + Then: + - The driver profile information is shown + - The messaging input is shown + */ + + /* + When: + - The driver picks up the rider + Then: + - The "Cancel Ride" button is shown + - The messaging input is shown + - The progress message is shown + */ + + /* + When: + - The driver drops off the rider + Then: + - The "Rate Driver" button is shown + - The messaging input is removed + - The progress message is removed + */ } } From 470e1209d602947579926a0b660b01f8c4896a8b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:33:28 -0500 Subject: [PATCH 13/73] Add PaymentMethod --- .../entities/PaymentMethod.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt new file mode 100644 index 0000000..11bb846 --- /dev/null +++ b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.entities + +/** + * + */ +data class PaymentMethod( + val id: String, +// val type: PaymentType, + val lastFour: String, + val expiration: ExpirationDate, + val cardholderName: String, + val paymentProvider: String, +// val billingAddress: Address +) From 0bd8f2c8c6697113660f8d72201df1a9e55dae07 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:50:42 -0500 Subject: [PATCH 14/73] Add PaymentSelectionView --- .../commonMain/kotlin/ui/PaymentSelection.kt | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt diff --git a/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt b/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt new file mode 100644 index 0000000..512fb25 --- /dev/null +++ b/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package ui + +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import org.pointyware.xyz.feature.ride.entities.PaymentMethod + +/** + * Represents the state of the payment selection view. + */ +sealed interface PaymentSelectionViewState { + data class PaymentSelected(val method: PaymentMethod?) : PaymentSelectionViewState + data class SelectPayment(val methods: List) : PaymentSelectionViewState +} + +/** + * Displays the currently selected payment (if any) and allows the user to select a + * different payment method. + */ +@Composable +fun PaymentSelectionView( + state: PaymentSelectionViewState, + modifier: Modifier = Modifier, + onSelectPayment: ()->Unit, + onPaymentSelected: (PaymentMethod)->Unit +) { + val contentDescription = remember(state) { when(state) { + is PaymentSelectionViewState.SelectPayment -> { + "Select Payment Method" + } + is PaymentSelectionViewState.PaymentSelected -> { + "Payment Method" + } + }} + Column( + modifier = modifier.semantics { + this.contentDescription = contentDescription + } + ) { + AnimatedContent(targetState = state) { targetState -> + when (targetState) { + is PaymentSelectionViewState.PaymentSelected -> { + val method = targetState.method + if (method != null) { + Text("Selected Payment Method: ${method.paymentProvider} *${method.lastFour}") + } else { + Text("No Payment Method Selected") + } + Button(onClick = onSelectPayment) { + Text("Select Payment Method") + } + } + is PaymentSelectionViewState.SelectPayment -> { + LazyColumn { + items(targetState.methods) { method -> + Button(onClick = { onPaymentSelected(method) }) { + Text("${method.paymentProvider} *${method.lastFour}") + } + } + } + } + } + } + } +} From edd4394c77894bc28e1d1c68f4869cda37fb8a39 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 10:53:11 -0500 Subject: [PATCH 15/73] Move PassengerDashboardView --- .../ui/PassengerDashboardView.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename feature/ride/src/commonMain/kotlin/{ => org.pointyware.xyz.feature.ride}/ui/PassengerDashboardView.kt (100%) diff --git a/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardView.kt b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/ui/PassengerDashboardView.kt rename to feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt From 640018ee604b2c6ec8de9a13fe56f09dcaca4687 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:09:24 -0500 Subject: [PATCH 16/73] Add payment repository --- .../feature/ride/data/PaymentRepository.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt new file mode 100644 index 0000000..188ce37 --- /dev/null +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.data + +import org.pointyware.xyz.feature.ride.entities.PaymentMethod + +interface PaymentRepository { + suspend fun getPaymentMethods(): List +} + +class PaymentRepositoryImpl( + +) : PaymentRepository { + + override suspend fun getPaymentMethods(): List { + TODO("Not yet implemented") + } +} From c71cc22cdedd010bdd8971ae83aa4b5bd7a9d10f Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:11:34 -0500 Subject: [PATCH 17/73] Add payment events --- .../kotlin/viewmodels/RideViewModel.kt | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt index 850b95e..d0a7500 100644 --- a/feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt @@ -14,7 +14,10 @@ import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.viewmodels.LoadingUiState import org.pointyware.xyz.core.viewmodels.MapViewModelImpl import org.pointyware.xyz.core.viewmodels.postError +import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.RideRequestRepository +import org.pointyware.xyz.feature.ride.entities.PaymentMethod +import ui.PaymentSelectionViewState /** * Maintains the state of a rider UI and provides actions to update it. @@ -22,7 +25,8 @@ import org.pointyware.xyz.feature.ride.data.RideRequestRepository * @see PassengerDashboardUiState */ class RideViewModel( - private val rideRequestRepository: RideRequestRepository + private val rideRequestRepository: RideRequestRepository, + private val paymentRepository: PaymentRepository ): MapViewModelImpl() { private val userLocation = Location( @@ -37,7 +41,7 @@ class RideViewModel( val state: StateFlow get() = mutableState fun startSearch() { - mutableState.value = PassengerDashboardUiState.Search("", emptyList()) + mutableState.value = PassengerDashboardUiState.Search("", emptyList(), PaymentSelectionViewState.PaymentSelected(null)) } fun updateQuery(query: String) { @@ -131,4 +135,27 @@ class RideViewModel( fun clearError() { mutableLoadingState.value = LoadingUiState.Idle() } + + fun onSelectPayment() { + viewModelScope.launch { + val methods = paymentRepository.getPaymentMethods() + mutableState.update { + if (it is PassengerDashboardUiState.Search) { + it.copy(paymentSelection = PaymentSelectionViewState.SelectPayment(methods)) + } else { + it + } + } + } + } + + fun onPaymentSelected(paymentMethod: PaymentMethod) { + mutableState.update { + if (it is PassengerDashboardUiState.Search) { + it.copy(paymentSelection = PaymentSelectionViewState.PaymentSelected(paymentMethod)) + } else { + it + } + } + } } From 31ca6545c311bd8bdc3c06340816a0e7f7f96582 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:11:48 -0500 Subject: [PATCH 18/73] Add payment method state to destination search ui state --- .../commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt b/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt index bba3d49..1ee2cfd 100644 --- a/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt +++ b/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt @@ -8,6 +8,7 @@ import org.pointyware.xyz.core.entities.business.Currency import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.geo.Route import org.pointyware.xyz.core.viewmodels.BriefProfileUiState +import ui.PaymentSelectionViewState /** * Represents the state of a rider's UI. @@ -24,7 +25,8 @@ sealed interface PassengerDashboardUiState { */ data class Search( val query: String = "", - val suggestions: List + val suggestions: List, + val paymentSelection: PaymentSelectionViewState ): PassengerDashboardUiState /** From 07d50ed2bdf1c4747e325353166ef06a1cd25c25 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:13:34 -0500 Subject: [PATCH 19/73] Bind payment method event callbacks to view model --- .../ui/PassengerDashboardView.kt | 7 +- .../kotlin/ui/PassengerDashboardScreen.kt | 4 +- .../commonMain/kotlin/ui/TripSearchView.kt | 79 +++++++++++-------- 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt index 251750f..bbe3058 100644 --- a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt +++ b/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt @@ -16,6 +16,7 @@ import org.pointyware.xyz.core.ui.LoadingResultView import org.pointyware.xyz.core.ui.MapView import org.pointyware.xyz.core.viewmodels.LoadingUiState import org.pointyware.xyz.core.viewmodels.MapUiState +import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState data class PassengerDashboardViewState( @@ -35,6 +36,8 @@ fun PassengerDashboardView( onUpdateQuery: (String)->Unit, onSendQuery: ()->Unit, onSelectLocation: (Location)->Unit, + onSelectPayment: ()->Unit, + onPaymentSelected: (PaymentMethod)->Unit, onConfirmDetails: ()->Unit, onCancel: ()->Unit, onBack: ()->Unit, @@ -67,7 +70,9 @@ fun PassengerDashboardView( onSendQuery = onSendQuery, onSelectLocation = onSelectLocation, onConfirmDetails = onConfirmDetails, - onCancelRequest = onCancel + onCancelRequest = onCancel, + onSelectPayment = onSelectPayment, + onPaymentSelected = onPaymentSelected ) } } diff --git a/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt b/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt index 3699c91..7de9eaa 100644 --- a/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt +++ b/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt @@ -35,6 +35,8 @@ fun PassengerDashboardScreen( onConfirmDetails = { viewModel.confirmDetails() }, onCancel = { viewModel.cancelRide() }, onBack = { navController.goBack() }, - clearError = { viewModel.clearError() } + clearError = { viewModel.clearError() }, + onSelectPayment = viewModel::onSelectPayment, + onPaymentSelected = viewModel::onPaymentSelected ) } diff --git a/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt index 4c2a0d3..a6e0512 100644 --- a/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt @@ -29,7 +29,9 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import org.pointyware.xyz.core.entities.geo.Location +import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState +import ui.PaymentSelectionView data class TripSearchViewState( val isExpanded: Boolean, @@ -49,7 +51,9 @@ fun TripSearchView( onSendQuery: ()->Unit, onSelectLocation: (Location)->Unit, onConfirmDetails: ()->Unit, - onCancelRequest: ()->Unit + onCancelRequest: ()->Unit, + onSelectPayment: () -> Unit, + onPaymentSelected: (PaymentMethod) -> Unit ) { val shape = when (state) { is PassengerDashboardUiState.Idle, @@ -78,7 +82,9 @@ fun TripSearchView( state = state, onUpdateSearch = onUpdateSearch, onSendQuery = onSendQuery, - onSelectLocation = onSelectLocation + onSelectLocation = onSelectLocation, + onSelectPayment = onSelectPayment, + onPaymentSelected = onPaymentSelected, ) } @@ -122,39 +128,48 @@ fun ActiveSearchView( state: PassengerDashboardUiState.Search, onUpdateSearch: (String)->Unit, onSendQuery: ()->Unit, - onSelectLocation: (Location)->Unit + onSelectLocation: (Location)->Unit, + onSelectPayment: ()->Unit, + onPaymentSelected: (PaymentMethod)->Unit ) { - Row( - modifier = Modifier.fillMaxWidth() - ) { - TextField( - value = state.query, - onValueChange = onUpdateSearch, - label = { Text("Search") }, - modifier = Modifier.weight(1f), + Column { + PaymentSelectionView( + state = state.paymentSelection, + onSelectPayment = onSelectPayment, + onPaymentSelected = onPaymentSelected ) - Button( - onClick = { - onSendQuery() - }, - enabled = state.query.isNotBlank() + Row( + modifier = Modifier.fillMaxWidth() ) { - Text("Confirm") - } - var expanded by remember(state.suggestions) { mutableStateOf(state.suggestions.isNotEmpty()) } - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.semantics { contentDescription = "Location Suggestions" }, - ) { - state.suggestions.forEach { suggestion -> - DropdownMenuItem( - text = { Text(suggestion.name) }, - onClick = { - expanded = false - onSelectLocation(suggestion) - } - ) + TextField( + value = state.query, + onValueChange = onUpdateSearch, + label = { Text("Search") }, + modifier = Modifier.weight(1f), + ) + Button( + onClick = { + onSendQuery() + }, + enabled = state.query.isNotBlank() + ) { + Text("Confirm") + } + var expanded by remember(state.suggestions) { mutableStateOf(state.suggestions.isNotEmpty()) } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.semantics { contentDescription = "Location Suggestions" }, + ) { + state.suggestions.forEach { suggestion -> + DropdownMenuItem( + text = { Text(suggestion.name) }, + onClick = { + expanded = false + onSelectLocation(suggestion) + } + ) + } } } } From d6111e483d597a48d3f64cb87827b08d8568c14e Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:14:18 -0500 Subject: [PATCH 20/73] Use state class as content key --- feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt b/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt index 512fb25..1d58301 100644 --- a/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt +++ b/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt @@ -49,7 +49,7 @@ fun PaymentSelectionView( this.contentDescription = contentDescription } ) { - AnimatedContent(targetState = state) { targetState -> + AnimatedContent(targetState = state, contentKey = { it::class }) { targetState -> when (targetState) { is PaymentSelectionViewState.PaymentSelected -> { val method = targetState.method From 1950f9ea00085273d6aa7896b2e043e73aeff621 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:14:53 -0500 Subject: [PATCH 21/73] Move RideViewModel --- .../pointyware/xyz/feature/ride}/viewmodels/RideViewModel.kt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/viewmodels/RideViewModel.kt (100%) diff --git a/feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/viewmodels/RideViewModel.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt From c50a871bdb7f4897b5f2e1726d3aaee20d74472b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:16:05 -0500 Subject: [PATCH 22/73] Move files from dot-separated package directory --- .../pointyware/xyz/feature/ride}/entities/ExpirationDate.kt | 2 +- .../pointyware/xyz/feature/ride}/entities/PaymentMethod.kt | 0 .../pointyware/xyz/feature/ride}/ui/PassengerDashboardView.kt | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename feature/ride/src/commonMain/kotlin/{org.pointyware.xyz.feature.ride => org/pointyware/xyz/feature/ride}/entities/ExpirationDate.kt (92%) rename feature/ride/src/commonMain/kotlin/{org.pointyware.xyz.feature.ride => org/pointyware/xyz/feature/ride}/entities/PaymentMethod.kt (100%) rename feature/ride/src/commonMain/kotlin/{org.pointyware.xyz.feature.ride => org/pointyware/xyz/feature/ride}/ui/PassengerDashboardView.kt (100%) diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/ExpirationDate.kt similarity index 92% rename from feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/ExpirationDate.kt index 1da3646..5b9087f 100644 --- a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/ExpirationDate.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/ExpirationDate.kt @@ -29,7 +29,7 @@ data class ExpirationDate( ) { init { require(month in MONTH_MIN..MONTH_MAX) { "Month must be between 1 and 12" } - require(year in YEAR_MIN .. YEAR_MAX) { "Year must be between 2024 and 3023" } + require(year in YEAR_MIN..YEAR_MAX) { "Year must be between 2024 and 3023" } } /** diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/entities/PaymentMethod.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt diff --git a/feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardView.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt From 2afa8c98a7dd2c0ac4679a264ae5deb1e579ea74 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:18:30 -0500 Subject: [PATCH 23/73] Move remaining files from root to package directory --- .../xyz/feature/ride}/data/DestinationSearchResult.kt | 0 .../pointyware/xyz/feature/ride}/data/RideRequestCache.kt | 0 .../pointyware/xyz/feature/ride}/data/RideRequestRepository.kt | 0 .../pointyware/xyz/feature/ride}/data/RideRequestService.kt | 0 .../pointyware/xyz/feature/ride}/di/RideDependencies.kt | 0 .../{ => org/pointyware/xyz/feature/ride}/di/RideModule.kt | 0 .../pointyware/xyz/feature/ride}/navigation/RideRouting.kt | 0 .../pointyware/xyz/feature/ride}/ui/PassengerDashboardScreen.kt | 0 .../pointyware/xyz/feature/ride}/ui/PassengerProfileView.kt | 0 .../pointyware/xyz/feature/ride}/ui/PaymentSelection.kt | 2 +- .../pointyware/xyz/feature/ride}/ui/RideUiStateMapper.kt | 0 .../{ => org/pointyware/xyz/feature/ride}/ui/TripSearchView.kt | 2 +- .../xyz/feature/ride}/viewmodels/PassengerDashboardUiState.kt | 2 +- .../org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt | 2 +- 14 files changed, 4 insertions(+), 4 deletions(-) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/data/DestinationSearchResult.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/data/RideRequestCache.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/data/RideRequestRepository.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/data/RideRequestService.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/di/RideDependencies.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/di/RideModule.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/navigation/RideRouting.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/ui/PassengerDashboardScreen.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/ui/PassengerProfileView.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/ui/PaymentSelection.kt (98%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/ui/RideUiStateMapper.kt (100%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/ui/TripSearchView.kt (99%) rename feature/ride/src/commonMain/kotlin/{ => org/pointyware/xyz/feature/ride}/viewmodels/PassengerDashboardUiState.kt (96%) diff --git a/feature/ride/src/commonMain/kotlin/data/DestinationSearchResult.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/DestinationSearchResult.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/data/DestinationSearchResult.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/DestinationSearchResult.kt diff --git a/feature/ride/src/commonMain/kotlin/data/RideRequestCache.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestCache.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/data/RideRequestCache.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestCache.kt diff --git a/feature/ride/src/commonMain/kotlin/data/RideRequestRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestRepository.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/data/RideRequestRepository.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestRepository.kt diff --git a/feature/ride/src/commonMain/kotlin/data/RideRequestService.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestService.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/data/RideRequestService.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestService.kt diff --git a/feature/ride/src/commonMain/kotlin/di/RideDependencies.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/di/RideDependencies.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt diff --git a/feature/ride/src/commonMain/kotlin/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/di/RideModule.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt diff --git a/feature/ride/src/commonMain/kotlin/navigation/RideRouting.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/navigation/RideRouting.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/navigation/RideRouting.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/navigation/RideRouting.kt diff --git a/feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/ui/PassengerDashboardScreen.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt diff --git a/feature/ride/src/commonMain/kotlin/ui/PassengerProfileView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerProfileView.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/ui/PassengerProfileView.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerProfileView.kt diff --git a/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt similarity index 98% rename from feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt index 1d58301..9741131 100644 --- a/feature/ride/src/commonMain/kotlin/ui/PaymentSelection.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt @@ -2,7 +2,7 @@ * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. */ -package ui +package org.pointyware.xyz.feature.ride.ui import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Column diff --git a/feature/ride/src/commonMain/kotlin/ui/RideUiStateMapper.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/RideUiStateMapper.kt similarity index 100% rename from feature/ride/src/commonMain/kotlin/ui/RideUiStateMapper.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/RideUiStateMapper.kt diff --git a/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt similarity index 99% rename from feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index a6e0512..c8171e5 100644 --- a/feature/ride/src/commonMain/kotlin/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState -import ui.PaymentSelectionView +import org.pointyware.xyz.feature.ride.ui.PaymentSelectionView data class TripSearchViewState( val isExpanded: Boolean, diff --git a/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt similarity index 96% rename from feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt index 1ee2cfd..6c9ccc4 100644 --- a/feature/ride/src/commonMain/kotlin/viewmodels/PassengerDashboardUiState.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt @@ -8,7 +8,7 @@ import org.pointyware.xyz.core.entities.business.Currency import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.geo.Route import org.pointyware.xyz.core.viewmodels.BriefProfileUiState -import ui.PaymentSelectionViewState +import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState /** * Represents the state of a rider's UI. diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt index d0a7500..5260f6d 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt @@ -17,7 +17,7 @@ import org.pointyware.xyz.core.viewmodels.postError import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.RideRequestRepository import org.pointyware.xyz.feature.ride.entities.PaymentMethod -import ui.PaymentSelectionViewState +import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState /** * Maintains the state of a rider UI and provides actions to update it. From 04dace61a6798261fce311c6cbdb96816d113051 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:28:39 -0500 Subject: [PATCH 24/73] Create local payment store --- .../xyz/feature/ride/local/PaymentStore.kt | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt new file mode 100644 index 0000000..b8d37d8 --- /dev/null +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.feature.ride.local + +import org.pointyware.xyz.feature.ride.entities.PaymentMethod + +/** + * A local store of payment methods. + */ +interface PaymentStore { + fun getPaymentMethods(): List + fun savePaymentMethod(paymentMethod: PaymentMethod) + fun removePaymentMethod(paymentMethod: PaymentMethod) +} + +class PaymentStoreImpl(): PaymentStore { + override fun getPaymentMethods(): List { + TODO("Not yet implemented") + } + + override fun savePaymentMethod(paymentMethod: PaymentMethod) { + TODO("Not yet implemented") + } + + override fun removePaymentMethod(paymentMethod: PaymentMethod) { + TODO("Not yet implemented") + } +} + +class TestPaymentStore( + private val methods: MutableList = mutableListOf() +): PaymentStore { + + override fun getPaymentMethods(): List { + return methods.toList() + } + + override fun savePaymentMethod(paymentMethod: PaymentMethod) { + methods.add(paymentMethod) + } + + override fun removePaymentMethod(paymentMethod: PaymentMethod) { + methods.remove(paymentMethod) + } +} From 3d742f8a39813fd208c66458c314bada859fdb9f Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:45:49 -0500 Subject: [PATCH 25/73] Expand actions of payment repository --- .../feature/ride/data/PaymentRepository.kt | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt index 188ce37..ffba6cd 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/PaymentRepository.kt @@ -5,16 +5,39 @@ package org.pointyware.xyz.feature.ride.data import org.pointyware.xyz.feature.ride.entities.PaymentMethod +import org.pointyware.xyz.feature.ride.local.PaymentStore interface PaymentRepository { - suspend fun getPaymentMethods(): List + suspend fun getPaymentMethods(): Result> + suspend fun savePaymentMethod(paymentMethod: PaymentMethod): Result + suspend fun removePaymentMethod(paymentMethod: PaymentMethod): Result } class PaymentRepositoryImpl( - + private val paymentStore: PaymentStore ) : PaymentRepository { - override suspend fun getPaymentMethods(): List { - TODO("Not yet implemented") + override suspend fun getPaymentMethods(): Result> { + return try { + Result.success(paymentStore.getPaymentMethods()) + } catch (e: Exception) { + Result.failure(e) + } + } + + override suspend fun savePaymentMethod(paymentMethod: PaymentMethod): Result { + return try { + Result.success(paymentStore.savePaymentMethod(paymentMethod)) + } catch (e: Exception) { + Result.failure(e) + } + } + + override suspend fun removePaymentMethod(paymentMethod: PaymentMethod): Result { + return try { + Result.success(paymentStore.removePaymentMethod(paymentMethod)) + } catch (e: Exception) { + Result.failure(e) + } } } From aab60f76b1086043cd8145552bc34d4e93fc133c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:50:06 -0500 Subject: [PATCH 26/73] update onSelectPayment to use Result --- .../feature/ride/viewmodels/RideViewModel.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt index 5260f6d..9489902 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt @@ -138,14 +138,19 @@ class RideViewModel( fun onSelectPayment() { viewModelScope.launch { - val methods = paymentRepository.getPaymentMethods() - mutableState.update { - if (it is PassengerDashboardUiState.Search) { - it.copy(paymentSelection = PaymentSelectionViewState.SelectPayment(methods)) - } else { - it + paymentRepository.getPaymentMethods() + .onSuccess { methods -> + mutableState.update { + if (it is PassengerDashboardUiState.Search) { + it.copy(paymentSelection = PaymentSelectionViewState.SelectPayment(methods)) + } else { + it + } + } + } + .onFailure { + it.printStackTrace() } - } } } From 896380bb271ab403c87bb471705358bcd7c0561c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:57:51 -0500 Subject: [PATCH 27/73] Add payment data types to ride module --- .../xyz/feature/ride/di/RideModule.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 65815a6..014fed0 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -8,8 +8,10 @@ import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf import org.koin.core.module.dsl.singleOf import org.koin.dsl.module -import org.pointyware.xyz.core.common.BuildInfo import org.pointyware.xyz.core.data.di.dataQualifier +import org.pointyware.xyz.core.entities.Uuid +import org.pointyware.xyz.feature.ride.data.PaymentRepository +import org.pointyware.xyz.feature.ride.data.PaymentRepositoryImpl import org.pointyware.xyz.feature.ride.data.RideRequestCache import org.pointyware.xyz.feature.ride.data.RideRequestCacheImpl import org.pointyware.xyz.feature.ride.data.RideRequestRepository @@ -17,6 +19,11 @@ import org.pointyware.xyz.feature.ride.data.RideRequestRepositoryImpl import org.pointyware.xyz.feature.ride.data.RideRequestService import org.pointyware.xyz.feature.ride.data.RideRequestServiceImpl import org.pointyware.xyz.feature.ride.data.TestRideRequestRepository +import org.pointyware.xyz.feature.ride.entities.ExpirationDate +import org.pointyware.xyz.feature.ride.entities.PaymentMethod +import org.pointyware.xyz.feature.ride.local.PaymentStore +import org.pointyware.xyz.feature.ride.local.PaymentStoreImpl +import org.pointyware.xyz.feature.ride.local.TestPaymentStore import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel /** @@ -39,8 +46,23 @@ fun featureRideDataModule() = module { singleOf(::RideRequestRepositoryImpl) { bind() } singleOf(::RideRequestCacheImpl) { bind() } singleOf(::RideRequestServiceImpl) { bind() } + + singleOf(::PaymentRepositoryImpl) { bind() } + singleOf(::PaymentStoreImpl) { bind() } } fun featureRideDataTestModule() = module { single { TestRideRequestRepository(dataScope = get(qualifier = dataQualifier)) } + + single { TestPaymentStore( + methods = mutableListOf( + PaymentMethod( + id = Uuid.v4(), + lastFour = "3456", + expiration = ExpirationDate(month = 12, year = 24), + cardholderName = "John Doe", + paymentProvider = "Bisa" + ) + ) + ) } } From 55c35621305c4c6e1d4b39adccbe2e8ff2e715ba Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 11:58:03 -0500 Subject: [PATCH 28/73] Change payment method id to uuid-v4 --- .../org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt index 11bb846..41b55ca 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/entities/PaymentMethod.kt @@ -4,11 +4,13 @@ package org.pointyware.xyz.feature.ride.entities +import org.pointyware.xyz.core.entities.Uuid + /** * */ data class PaymentMethod( - val id: String, + val id: Uuid, // val type: PaymentType, val lastFour: String, val expiration: ExpirationDate, From 18a40f8de81cee3f095571ec8c0f6eb653a6ae4c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:00:37 -0500 Subject: [PATCH 29/73] Fix invalid expiration date year --- .../kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 014fed0..40d6911 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -59,7 +59,7 @@ fun featureRideDataTestModule() = module { PaymentMethod( id = Uuid.v4(), lastFour = "3456", - expiration = ExpirationDate(month = 12, year = 24), + expiration = ExpirationDate(month = 12, year = 2024), cardholderName = "John Doe", paymentProvider = "Bisa" ) From 3cc63d4d95f758a1c0ade51d7fc379751aaed4c0 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:00:51 -0500 Subject: [PATCH 30/73] Fix missing home route --- .../ui/PassengerDashboardScreenUiTest.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index b9c584b..c7ce75b 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -20,11 +20,14 @@ import androidx.compose.ui.test.runComposeUiTest import androidx.compose.ui.test.waitUntilDoesNotExist import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin +import org.koin.dsl.module import org.koin.mp.KoinPlatform.getKoin import org.pointyware.xyz.core.navigation.StackNavigationController +import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule +import org.pointyware.xyz.feature.ride.navigation.rideRoute import org.pointyware.xyz.feature.ride.test.setupKoin import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel @@ -43,7 +46,10 @@ class PassengerDashboardScreenUiTest { fun setUp() { setupKoin() loadKoinModules(listOf( - featureRideDataTestModule() + featureRideDataTestModule(), + module { + single(qualifier = homeQualifier) { rideRoute } + } )) } From 4f09cb43763e8750a619509358343505756670df Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:03:50 -0500 Subject: [PATCH 31/73] Fix wrong content description --- .../org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt index 9741131..a4a5485 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PaymentSelection.kt @@ -38,7 +38,7 @@ fun PaymentSelectionView( ) { val contentDescription = remember(state) { when(state) { is PaymentSelectionViewState.SelectPayment -> { - "Select Payment Method" + "Payment Method Selection" } is PaymentSelectionViewState.PaymentSelected -> { "Payment Method" From ee1aaf93b0421b133753112f4f95e640973a46e9 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:05:05 -0500 Subject: [PATCH 32/73] Update content description expectations for consistency --- .../ui/PassengerDashboardScreenUiTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index c7ce75b..469b5d9 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -108,7 +108,7 @@ class PassengerDashboardScreenUiTest { onNodeWithText("Confirm") .assertExists() .assertIsNotEnabled() - onNodeWithContentDescription("Payment Selection") + onNodeWithContentDescription("Payment Method") .assertExists() /* @@ -118,7 +118,7 @@ class PassengerDashboardScreenUiTest { - The "Payment Selection" button transforms into the Payment Method Selection */ - onNodeWithContentDescription("Payment Selection") + onNodeWithContentDescription("Select Payment Method") .performClick() onNodeWithContentDescription("Payment Method Selection") From f0a497b34feaae9e5bb7532de95115b7c7a03de1 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:06:14 -0500 Subject: [PATCH 33/73] Fix button expectation --- .../ui/PassengerDashboardScreenUiTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 469b5d9..7302c91 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -118,7 +118,7 @@ class PassengerDashboardScreenUiTest { - The "Payment Selection" button transforms into the Payment Method Selection */ - onNodeWithContentDescription("Select Payment Method") + onNodeWithText("Select Payment Method") .performClick() onNodeWithContentDescription("Payment Method Selection") From 910605ea2cbc4724a2b57de9e471806fc7279ea6 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:08:04 -0500 Subject: [PATCH 34/73] Fix test expectations --- .../ui/PassengerDashboardScreenUiTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 7302c91..ae494c3 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -128,16 +128,18 @@ class PassengerDashboardScreenUiTest { When: - User selects a payment method - Bisa Then: - - The "Payment Method Selection" button transforms back into the "Payment Selection" button + - The "Payment Method Selection" form transforms back into the "Payment Method" form - The selected payment method is shown */ onNodeWithText("Bisa", substring = true) .performClick() - onNodeWithContentDescription("Payment Selection") + onNodeWithContentDescription("Payment Method") .assertExists() onNodeWithText("Bisa", substring = true) .assertExists() + onNodeWithText("Select Payment Method") + .assertExists() /* When: From 2377793c44acce6ba3dd17de21bc501f3fc8b480 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:43:20 -0500 Subject: [PATCH 35/73] Add arrived rider state --- .../pointyware/xyz/feature/ride/ui/TripSearchView.kt | 12 ++++++++++++ .../ride/viewmodels/PassengerDashboardUiState.kt | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index c8171e5..151cf21 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -109,6 +109,10 @@ fun TripSearchView( is PassengerDashboardUiState.Riding -> { ActiveRideView(state = state) } + + is PassengerDashboardUiState.Arrived -> { + CompletedRideView(state = state) + } } } } @@ -234,3 +238,11 @@ fun ActiveRideView( // Do nothing // TODO: rider details } + +@Composable +fun CompletedRideView( + state: PassengerDashboardUiState.Arrived, + modifier: Modifier = Modifier, +) { + +} diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt index 6c9ccc4..fa36eab 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt @@ -70,4 +70,12 @@ sealed interface PassengerDashboardUiState { val route: Route, val eta: Int ): PassengerDashboardUiState + + /** + * The rider has arrived at the destination. + */ + data class Arrived( + val driver: BriefProfileUiState, + val route: Route + ): PassengerDashboardUiState } From 1c9b33f6e5f152653801e05983f19e14055931b8 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:52:26 -0500 Subject: [PATCH 36/73] Rename RideRequestRepository to reflect generalized purpose --- .../{RideRequestRepository.kt => TripRepository.kt} | 10 +++++----- .../org/pointyware/xyz/feature/ride/di/RideModule.kt | 10 +++++----- .../xyz/feature/ride/viewmodels/RideViewModel.kt | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) rename feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/{RideRequestRepository.kt => TripRepository.kt} (96%) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt similarity index 96% rename from feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestRepository.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 369a58d..3a3bdae 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -15,7 +15,7 @@ import kotlin.time.Duration.Companion.milliseconds /** * Handles requests for rides. */ -interface RideRequestRepository { +interface TripRepository { suspend fun searchDestinations(query: String): Result suspend fun findRoute(origin: Location, destination: Location): Result suspend fun requestRide(route: Route): Result @@ -25,10 +25,10 @@ interface RideRequestRepository { /** * */ -class RideRequestRepositoryImpl( +class TripRepositoryImpl( private val cache: RideRequestCache, private val service: RideRequestService, -): RideRequestRepository { +): TripRepository { override suspend fun searchDestinations(query: String): Result { return service.searchDestinations(query) @@ -56,10 +56,10 @@ class RideRequestRepositoryImpl( /** * */ -class TestRideRequestRepository( +class TestTripRepository( val destinations: MutableSet = mutableSetOf(), val dataScope: CoroutineScope, -): RideRequestRepository { +): TripRepository { private val maximumLevenshteinDistance = 20 diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 40d6911..b7af6d4 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -14,11 +14,11 @@ import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.PaymentRepositoryImpl import org.pointyware.xyz.feature.ride.data.RideRequestCache import org.pointyware.xyz.feature.ride.data.RideRequestCacheImpl -import org.pointyware.xyz.feature.ride.data.RideRequestRepository -import org.pointyware.xyz.feature.ride.data.RideRequestRepositoryImpl +import org.pointyware.xyz.feature.ride.data.TripRepository +import org.pointyware.xyz.feature.ride.data.TripRepositoryImpl import org.pointyware.xyz.feature.ride.data.RideRequestService import org.pointyware.xyz.feature.ride.data.RideRequestServiceImpl -import org.pointyware.xyz.feature.ride.data.TestRideRequestRepository +import org.pointyware.xyz.feature.ride.data.TestTripRepository import org.pointyware.xyz.feature.ride.entities.ExpirationDate import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.local.PaymentStore @@ -43,7 +43,7 @@ fun featureRideViewModelModule() = module { } fun featureRideDataModule() = module { - singleOf(::RideRequestRepositoryImpl) { bind() } + singleOf(::TripRepositoryImpl) { bind() } singleOf(::RideRequestCacheImpl) { bind() } singleOf(::RideRequestServiceImpl) { bind() } @@ -52,7 +52,7 @@ fun featureRideDataModule() = module { } fun featureRideDataTestModule() = module { - single { TestRideRequestRepository(dataScope = get(qualifier = dataQualifier)) } + single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } single { TestPaymentStore( methods = mutableListOf( diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt index 9489902..0e82e3c 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt @@ -15,7 +15,7 @@ import org.pointyware.xyz.core.viewmodels.LoadingUiState import org.pointyware.xyz.core.viewmodels.MapViewModelImpl import org.pointyware.xyz.core.viewmodels.postError import org.pointyware.xyz.feature.ride.data.PaymentRepository -import org.pointyware.xyz.feature.ride.data.RideRequestRepository +import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState @@ -25,7 +25,7 @@ import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState * @see PassengerDashboardUiState */ class RideViewModel( - private val rideRequestRepository: RideRequestRepository, + private val tripRepository: TripRepository, private val paymentRepository: PaymentRepository ): MapViewModelImpl() { @@ -72,7 +72,7 @@ class RideViewModel( private fun findRoute(start: Location, end: Location) { mutableLoadingState.value = LoadingUiState.Loading() viewModelScope.launch { - rideRequestRepository.findRoute(start, end) + tripRepository.findRoute(start, end) .onSuccess { route -> // TODO: Calculate route and price; update state val rate = 120 // $1.20 per km From ba7ad67d832e21fdd3a4818bbe643fae744e45bd Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:52:33 -0500 Subject: [PATCH 37/73] Reorganize test setup --- .../ui/PassengerDashboardScreenUiTest.kt | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index ae494c3..20e042b 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -22,10 +22,11 @@ import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin import org.koin.dsl.module import org.koin.mp.KoinPlatform.getKoin -import org.pointyware.xyz.core.navigation.StackNavigationController +import org.pointyware.xyz.core.navigation.XyzNavController import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies +import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule import org.pointyware.xyz.feature.ride.navigation.rideRoute import org.pointyware.xyz.feature.ride.test.setupKoin @@ -42,6 +43,11 @@ import kotlin.test.assertEquals @OptIn(ExperimentalTestApi::class) class PassengerDashboardScreenUiTest { + private lateinit var tripRepository: TripRepository + + private lateinit var viewModel: RideViewModel + private lateinit var navController: XyzNavController + @BeforeTest fun setUp() { setupKoin() @@ -51,6 +57,10 @@ class PassengerDashboardScreenUiTest { single(qualifier = homeQualifier) { rideRoute } } )) + + val koin = getKoin() + viewModel = koin.get() + navController = koin.get() } @AfterTest @@ -60,22 +70,19 @@ class PassengerDashboardScreenUiTest { @Test fun request_ride() = runComposeUiTest { - val di = getKoin() - val viewModel = di.get() - val navController = di.get>() - - assertEquals(PassengerDashboardUiState.Idle, viewModel.state.value, "Initial state is Idle") - /* Given: - User is on the Ride Screen - UiState is Idle + */ + assertEquals(PassengerDashboardUiState.Idle, viewModel.state.value, "Initial state is Idle") + + /* When: - - No Events + - The Passenger Dashboard Screen is shown Then: - The "New Ride" button is shown */ - setContent { XyzTheme( uiDependencies = EmptyTestUiDependencies() @@ -218,7 +225,11 @@ class PassengerDashboardScreenUiTest { Then: - The driver profile information is shown - The messaging input is shown + - The driver arriving message is shown */ + // TODO: trigger ride accepted event in repo + + /* When: @@ -226,8 +237,9 @@ class PassengerDashboardScreenUiTest { Then: - The "Cancel Ride" button is shown - The messaging input is shown - - The progress message is shown + - The driver delivery message is shown */ + // TODO: trigger ride picked up event in repo /* When: @@ -237,5 +249,7 @@ class PassengerDashboardScreenUiTest { - The messaging input is removed - The progress message is removed */ + // TODO: trigger ride completed event in repo + } } From 9cfc40544360d2ebbf1b05bb090149043239130c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:54:42 -0500 Subject: [PATCH 38/73] Add current trip observable --- .../xyz/feature/ride/data/TripRepository.kt | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 3a3bdae..6c69da5 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -6,6 +6,7 @@ package org.pointyware.xyz.feature.ride.data import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.StateFlow import kotlinx.datetime.Instant import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.ride.Ride @@ -13,9 +14,14 @@ import org.pointyware.xyz.core.entities.geo.Route import kotlin.time.Duration.Companion.milliseconds /** - * Handles requests for rides. + * Handles trip search and scheduling. */ interface TripRepository { + /** + * The current trip being taken by the user. + */ + val currentTrip: StateFlow + suspend fun searchDestinations(query: String): Result suspend fun findRoute(origin: Location, destination: Location): Result suspend fun requestRide(route: Route): Result @@ -30,6 +36,9 @@ class TripRepositoryImpl( private val service: RideRequestService, ): TripRepository { + override val currentTrip: StateFlow + get() = TODO("Not yet implemented") + override suspend fun searchDestinations(query: String): Result { return service.searchDestinations(query) .onSuccess { @@ -61,6 +70,9 @@ class TestTripRepository( val dataScope: CoroutineScope, ): TripRepository { + override val currentTrip: StateFlow + get() = TODO("Not yet implemented") + private val maximumLevenshteinDistance = 20 /** From ad46d080a3642073a73cea575a17516f63e202ff Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 12:55:40 -0500 Subject: [PATCH 39/73] rename service/cache --- .../ride/data/{RideRequestCache.kt => TripCache.kt} | 6 +++--- .../xyz/feature/ride/data/TripRepository.kt | 4 ++-- .../data/{RideRequestService.kt => TripService.kt} | 4 ++-- .../org/pointyware/xyz/feature/ride/di/RideModule.kt | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) rename feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/{RideRequestCache.kt => TripCache.kt} (91%) rename feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/{RideRequestService.kt => TripService.kt} (90%) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestCache.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripCache.kt similarity index 91% rename from feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestCache.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripCache.kt index 403ea2a..cf01ec4 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestCache.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripCache.kt @@ -7,15 +7,15 @@ package org.pointyware.xyz.feature.ride.data /** * */ -interface RideRequestCache { +interface TripCache { suspend fun saveDestinations(query: String, searchResult: DestinationSearchResult) suspend fun getDestinations(query: String): DestinationSearchResult? suspend fun dropDestinations(query: String) } -class RideRequestCacheImpl( +class TripCacheImpl( -): RideRequestCache { +): TripCache { private val queryCache = mutableMapOf() diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 6c69da5..f138966 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -32,8 +32,8 @@ interface TripRepository { * */ class TripRepositoryImpl( - private val cache: RideRequestCache, - private val service: RideRequestService, + private val cache: TripCache, + private val service: TripService, ): TripRepository { override val currentTrip: StateFlow diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestService.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripService.kt similarity index 90% rename from feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestService.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripService.kt index 0bcc295..9bf7960 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/RideRequestService.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripService.kt @@ -9,7 +9,7 @@ import org.pointyware.xyz.core.entities.ride.Ride /** * Defines actions that can be performed on a remote service to request rides. */ -interface RideRequestService { +interface TripService { /** * Searches for destinations that match the given query. @@ -22,7 +22,7 @@ interface RideRequestService { suspend fun postRide(ride: Ride): Result } -class RideRequestServiceImpl : RideRequestService { +class TripServiceImpl : TripService { override suspend fun searchDestinations(query: String): Result { TODO() diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index b7af6d4..6b7d544 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -12,12 +12,12 @@ import org.pointyware.xyz.core.data.di.dataQualifier import org.pointyware.xyz.core.entities.Uuid import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.PaymentRepositoryImpl -import org.pointyware.xyz.feature.ride.data.RideRequestCache -import org.pointyware.xyz.feature.ride.data.RideRequestCacheImpl +import org.pointyware.xyz.feature.ride.data.TripCache +import org.pointyware.xyz.feature.ride.data.TripCacheImpl import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.data.TripRepositoryImpl -import org.pointyware.xyz.feature.ride.data.RideRequestService -import org.pointyware.xyz.feature.ride.data.RideRequestServiceImpl +import org.pointyware.xyz.feature.ride.data.TripService +import org.pointyware.xyz.feature.ride.data.TripServiceImpl import org.pointyware.xyz.feature.ride.data.TestTripRepository import org.pointyware.xyz.feature.ride.entities.ExpirationDate import org.pointyware.xyz.feature.ride.entities.PaymentMethod @@ -44,8 +44,8 @@ fun featureRideViewModelModule() = module { fun featureRideDataModule() = module { singleOf(::TripRepositoryImpl) { bind() } - singleOf(::RideRequestCacheImpl) { bind() } - singleOf(::RideRequestServiceImpl) { bind() } + singleOf(::TripCacheImpl) { bind() } + singleOf(::TripServiceImpl) { bind() } singleOf(::PaymentRepositoryImpl) { bind() } singleOf(::PaymentStoreImpl) { bind() } From 69c0d123ec92aac9f8522981d2ad48049f2f529c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:00:13 -0500 Subject: [PATCH 40/73] Move test data to test setup --- .../xyz/feature/ride/di/RideModule.kt | 12 +----------- .../ui/PassengerDashboardScreenUiTest.kt | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 6b7d544..729f190 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -54,15 +54,5 @@ fun featureRideDataModule() = module { fun featureRideDataTestModule() = module { single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } - single { TestPaymentStore( - methods = mutableListOf( - PaymentMethod( - id = Uuid.v4(), - lastFour = "3456", - expiration = ExpirationDate(month = 12, year = 2024), - cardholderName = "John Doe", - paymentProvider = "Bisa" - ) - ) - ) } + single { TestPaymentStore() } } diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 20e042b..7c27f85 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -22,12 +22,16 @@ import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin import org.koin.dsl.module import org.koin.mp.KoinPlatform.getKoin +import org.pointyware.xyz.core.entities.Uuid import org.pointyware.xyz.core.navigation.XyzNavController import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule +import org.pointyware.xyz.feature.ride.entities.ExpirationDate +import org.pointyware.xyz.feature.ride.entities.PaymentMethod +import org.pointyware.xyz.feature.ride.local.TestPaymentStore import org.pointyware.xyz.feature.ride.navigation.rideRoute import org.pointyware.xyz.feature.ride.test.setupKoin import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState @@ -44,6 +48,7 @@ import kotlin.test.assertEquals class PassengerDashboardScreenUiTest { private lateinit var tripRepository: TripRepository + private lateinit var paymentStore: TestPaymentStore private lateinit var viewModel: RideViewModel private lateinit var navController: XyzNavController @@ -59,6 +64,19 @@ class PassengerDashboardScreenUiTest { )) val koin = getKoin() + + tripRepository = koin.get() + paymentStore = koin.get() as TestPaymentStore + paymentStore.savePaymentMethod( + PaymentMethod( + id = Uuid.v4(), + lastFour = "3456", + expiration = ExpirationDate(month = 12, year = 2024), + cardholderName = "John Doe", + paymentProvider = "Bisa" + ) + ) + viewModel = koin.get() navController = koin.get() } From b3b797271409f0912867502409aa3a3d67c41603 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:01:18 -0500 Subject: [PATCH 41/73] Make test payment store accessible with test type --- .../kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt | 3 ++- .../ui/PassengerDashboardScreenUiTest.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 729f190..04e1765 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -54,5 +54,6 @@ fun featureRideDataModule() = module { fun featureRideDataTestModule() = module { single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } - single { TestPaymentStore() } + single { TestPaymentStore() } + single { get() } } diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 7c27f85..5f7036f 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -66,7 +66,7 @@ class PassengerDashboardScreenUiTest { val koin = getKoin() tripRepository = koin.get() - paymentStore = koin.get() as TestPaymentStore + paymentStore = koin.get() paymentStore.savePaymentMethod( PaymentMethod( id = Uuid.v4(), From 1ae5ffe51c7ebd7e56f9bed6e0de5922661cf62b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:01:32 -0500 Subject: [PATCH 42/73] rename TestPaymentStore --- .../org/pointyware/xyz/feature/ride/di/RideModule.kt | 9 +++------ .../pointyware/xyz/feature/ride/local/PaymentStore.kt | 2 +- .../ui/PassengerDashboardScreenUiTest.kt | 4 ++-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index 04e1765..b0ef16b 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -9,7 +9,6 @@ import org.koin.core.module.dsl.factoryOf import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import org.pointyware.xyz.core.data.di.dataQualifier -import org.pointyware.xyz.core.entities.Uuid import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.PaymentRepositoryImpl import org.pointyware.xyz.feature.ride.data.TripCache @@ -19,11 +18,9 @@ import org.pointyware.xyz.feature.ride.data.TripRepositoryImpl import org.pointyware.xyz.feature.ride.data.TripService import org.pointyware.xyz.feature.ride.data.TripServiceImpl import org.pointyware.xyz.feature.ride.data.TestTripRepository -import org.pointyware.xyz.feature.ride.entities.ExpirationDate -import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.local.PaymentStore import org.pointyware.xyz.feature.ride.local.PaymentStoreImpl -import org.pointyware.xyz.feature.ride.local.TestPaymentStore +import org.pointyware.xyz.feature.ride.local.FakePaymentStore import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel /** @@ -54,6 +51,6 @@ fun featureRideDataModule() = module { fun featureRideDataTestModule() = module { single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } - single { TestPaymentStore() } - single { get() } + single { FakePaymentStore() } + single { get() } } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt index b8d37d8..ab1890e 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/local/PaymentStore.kt @@ -29,7 +29,7 @@ class PaymentStoreImpl(): PaymentStore { } } -class TestPaymentStore( +class FakePaymentStore( private val methods: MutableList = mutableListOf() ): PaymentStore { diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 5f7036f..0bb2084 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -31,7 +31,7 @@ import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule import org.pointyware.xyz.feature.ride.entities.ExpirationDate import org.pointyware.xyz.feature.ride.entities.PaymentMethod -import org.pointyware.xyz.feature.ride.local.TestPaymentStore +import org.pointyware.xyz.feature.ride.local.FakePaymentStore import org.pointyware.xyz.feature.ride.navigation.rideRoute import org.pointyware.xyz.feature.ride.test.setupKoin import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState @@ -48,7 +48,7 @@ import kotlin.test.assertEquals class PassengerDashboardScreenUiTest { private lateinit var tripRepository: TripRepository - private lateinit var paymentStore: TestPaymentStore + private lateinit var paymentStore: FakePaymentStore private lateinit var viewModel: RideViewModel private lateinit var navController: XyzNavController From 000c110a645059a3fde74285cb8c8fc7f001061b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:06:00 -0500 Subject: [PATCH 43/73] implement fake request ride --- .../xyz/feature/ride/data/TripRepository.kt | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index f138966..2004bbc 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -6,11 +6,20 @@ package org.pointyware.xyz.feature.ride.data import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.datetime.Clock import kotlinx.datetime.Instant +import org.pointyware.xyz.core.entities.Name +import org.pointyware.xyz.core.entities.Uuid +import org.pointyware.xyz.core.entities.data.Uri import org.pointyware.xyz.core.entities.geo.Location -import org.pointyware.xyz.core.entities.ride.Ride import org.pointyware.xyz.core.entities.geo.Route +import org.pointyware.xyz.core.entities.profile.Gender +import org.pointyware.xyz.core.entities.profile.RiderProfile +import org.pointyware.xyz.core.entities.ride.Ride +import org.pointyware.xyz.core.entities.ride.planRide import kotlin.time.Duration.Companion.milliseconds /** @@ -70,8 +79,9 @@ class TestTripRepository( val dataScope: CoroutineScope, ): TripRepository { + private val mutableCurrentTrip = MutableStateFlow(null as Ride?) override val currentTrip: StateFlow - get() = TODO("Not yet implemented") + get() = mutableCurrentTrip.asStateFlow() private val maximumLevenshteinDistance = 20 @@ -138,11 +148,21 @@ class TestTripRepository( } override suspend fun requestRide(route: Route): Result { - TODO("Not yet implemented") -// mutableNewRides.emit(ride) -// mutablePostedRides.update { it + ride } -// // no limiting criteria in tests -// return Result.success(ride) + val plannedRide = planRide( + id = Uuid.v4(), + rider = RiderProfile( + id = Uuid.v4(), + name = Name("Test", "", "Rider"), + gender = Gender.Man, + picture = Uri.nullDevice, + preferences = "", + disabilities = emptySet() + ), + plannedRoute = route, + timePosted = Clock.System.now(), + ) + mutableCurrentTrip.value = plannedRide + return Result.success(plannedRide) } override suspend fun scheduleRide(route: Route, time: Instant): Result { From 117f370b10d8cafd0da79a685fd1650b83f9ff7d Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:09:40 -0500 Subject: [PATCH 44/73] Move passenger profile to test setup --- .../xyz/feature/ride/data/TripRepository.kt | 11 +++-------- .../pointyware/xyz/feature/ride/di/RideModule.kt | 3 ++- .../ui/PassengerDashboardScreenUiTest.kt | 15 ++++++++++++++- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 2004bbc..3bf8eeb 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -79,6 +79,8 @@ class TestTripRepository( val dataScope: CoroutineScope, ): TripRepository { + lateinit var riderProfile: RiderProfile + private val mutableCurrentTrip = MutableStateFlow(null as Ride?) override val currentTrip: StateFlow get() = mutableCurrentTrip.asStateFlow() @@ -150,14 +152,7 @@ class TestTripRepository( override suspend fun requestRide(route: Route): Result { val plannedRide = planRide( id = Uuid.v4(), - rider = RiderProfile( - id = Uuid.v4(), - name = Name("Test", "", "Rider"), - gender = Gender.Man, - picture = Uri.nullDevice, - preferences = "", - disabilities = emptySet() - ), + rider = riderProfile, plannedRoute = route, timePosted = Clock.System.now(), ) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index b0ef16b..def3fec 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -49,7 +49,8 @@ fun featureRideDataModule() = module { } fun featureRideDataTestModule() = module { - single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } + single { TestTripRepository(dataScope = get(qualifier = dataQualifier)) } + single { get() } single { FakePaymentStore() } single { get() } diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 0bb2084..b76a457 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -22,11 +22,16 @@ import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin import org.koin.dsl.module import org.koin.mp.KoinPlatform.getKoin +import org.pointyware.xyz.core.entities.Name import org.pointyware.xyz.core.entities.Uuid +import org.pointyware.xyz.core.entities.data.Uri +import org.pointyware.xyz.core.entities.profile.Gender +import org.pointyware.xyz.core.entities.profile.RiderProfile import org.pointyware.xyz.core.navigation.XyzNavController import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies +import org.pointyware.xyz.feature.ride.data.TestTripRepository import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule import org.pointyware.xyz.feature.ride.entities.ExpirationDate @@ -47,7 +52,7 @@ import kotlin.test.assertEquals @OptIn(ExperimentalTestApi::class) class PassengerDashboardScreenUiTest { - private lateinit var tripRepository: TripRepository + private lateinit var tripRepository: TestTripRepository private lateinit var paymentStore: FakePaymentStore private lateinit var viewModel: RideViewModel @@ -66,6 +71,14 @@ class PassengerDashboardScreenUiTest { val koin = getKoin() tripRepository = koin.get() + tripRepository.riderProfile = RiderProfile( + id = Uuid.v4(), + name = Name("Test", "", "Rider"), + gender = Gender.Man, + picture = Uri.nullDevice, + preferences = "", + disabilities = emptySet() + ) paymentStore = koin.get() paymentStore.savePaymentMethod( PaymentMethod( From 1f4b413bc5de54dfbcffb204f597ccbadbbaf392 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:18:59 -0500 Subject: [PATCH 45/73] Add testing event functions --- .../xyz/feature/ride/data/TripRepository.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 3bf8eeb..d03450c 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -11,12 +11,10 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.datetime.Clock import kotlinx.datetime.Instant -import org.pointyware.xyz.core.entities.Name import org.pointyware.xyz.core.entities.Uuid -import org.pointyware.xyz.core.entities.data.Uri import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.geo.Route -import org.pointyware.xyz.core.entities.profile.Gender +import org.pointyware.xyz.core.entities.profile.DriverProfile import org.pointyware.xyz.core.entities.profile.RiderProfile import org.pointyware.xyz.core.entities.ride.Ride import org.pointyware.xyz.core.entities.ride.planRide @@ -163,4 +161,16 @@ class TestTripRepository( override suspend fun scheduleRide(route: Route, time: Instant): Result { TODO("Not yet implemented") } + + fun acceptRequest(driverProfile: DriverProfile) { + TODO("Not yet implemented") + } + + fun pickUpRider() { + TODO() + } + + fun dropOffRider() { + TODO() + } } From f39259bb83c5bb117b54774a03a46a6d206ecdfe Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:19:18 -0500 Subject: [PATCH 46/73] Finish test script --- .../ui/PassengerDashboardScreenUiTest.kt | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index b76a457..a895dc9 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -24,9 +24,12 @@ import org.koin.dsl.module import org.koin.mp.KoinPlatform.getKoin import org.pointyware.xyz.core.entities.Name import org.pointyware.xyz.core.entities.Uuid +import org.pointyware.xyz.core.entities.business.Individual import org.pointyware.xyz.core.entities.data.Uri +import org.pointyware.xyz.core.entities.profile.DriverProfile import org.pointyware.xyz.core.entities.profile.Gender import org.pointyware.xyz.core.entities.profile.RiderProfile +import org.pointyware.xyz.core.entities.ride.Accommodation import org.pointyware.xyz.core.navigation.XyzNavController import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme @@ -58,6 +61,8 @@ class PassengerDashboardScreenUiTest { private lateinit var viewModel: RideViewModel private lateinit var navController: XyzNavController + private lateinit var driverProfile: DriverProfile + @BeforeTest fun setUp() { setupKoin() @@ -92,6 +97,15 @@ class PassengerDashboardScreenUiTest { viewModel = koin.get() navController = koin.get() + + driverProfile = DriverProfile( + id = Uuid.v4(), + name = Name("Test", "", "Driver"), + gender = Gender.Woman, + picture = Uri.nullDevice, + accommodations = setOf(Accommodation.AnimalFriendly), + business = Individual + ) } @AfterTest @@ -258,19 +272,37 @@ class PassengerDashboardScreenUiTest { - The messaging input is shown - The driver arriving message is shown */ - // TODO: trigger ride accepted event in repo - + tripRepository.acceptRequest(driverProfile) + onNodeWithContentDescription("Driver Profile") + .assertExists() + onNodeWithContentDescription("Message Input") + .assertExists() + onNodeWithText("Animal Friendly") + .assertExists() + onNodeWithText("Driver is on the way") + .assertExists() /* When: - The driver picks up the rider Then: - The "Cancel Ride" button is shown + - The driver profile information is shown - The messaging input is shown - The driver delivery message is shown */ - // TODO: trigger ride picked up event in repo + tripRepository.pickUpRider() + + onNodeWithText("Cancel Ride") + .assertExists() + .assertIsEnabled() + onNodeWithContentDescription("Driver Profile") + .assertExists() + onNodeWithContentDescription("Message Input") + .assertExists() + onNodeWithText("You're on your way!") + .assertExists() /* When: @@ -279,8 +311,22 @@ class PassengerDashboardScreenUiTest { - The "Rate Driver" button is shown - The messaging input is removed - The progress message is removed + - The "You've arrived!" message is shown + - The "Done" button is shown */ - // TODO: trigger ride completed event in repo + tripRepository.dropOffRider() + onNodeWithText("Rate Driver") + .assertExists() + .assertIsEnabled() + onNodeWithContentDescription("Message Input") + .assertDoesNotExist() + onNodeWithText("You're on your way!") + .assertDoesNotExist() + onNodeWithText("You've arrived!") + .assertExists() + onNodeWithText("Done") + .assertExists() + .assertIsEnabled() } } From 4e6f310e9a67c3e413d52da5568bd3791cda4f78 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:47:15 -0500 Subject: [PATCH 47/73] implement test repository functions --- .../xyz/feature/ride/data/TripRepository.kt | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index d03450c..4dcf61e 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update import kotlinx.datetime.Clock import kotlinx.datetime.Instant import org.pointyware.xyz.core.entities.Uuid @@ -16,6 +17,9 @@ import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.geo.Route import org.pointyware.xyz.core.entities.profile.DriverProfile import org.pointyware.xyz.core.entities.profile.RiderProfile +import org.pointyware.xyz.core.entities.ride.ActiveRide +import org.pointyware.xyz.core.entities.ride.PendingRide +import org.pointyware.xyz.core.entities.ride.PlannedRide import org.pointyware.xyz.core.entities.ride.Ride import org.pointyware.xyz.core.entities.ride.planRide import kotlin.time.Duration.Companion.milliseconds @@ -163,14 +167,35 @@ class TestTripRepository( } fun acceptRequest(driverProfile: DriverProfile) { - TODO("Not yet implemented") + mutableCurrentTrip.update { + when (it) { + is PlannedRide -> { + it.accept(driverProfile, Clock.System.now()) + } + else -> it + } + } } fun pickUpRider() { - TODO() + mutableCurrentTrip.update { + when (it) { + is PendingRide -> { + it.arrive(Clock.System.now()) + } + else -> it + } + } } fun dropOffRider() { - TODO() + mutableCurrentTrip.update { + when (it) { + is ActiveRide -> { + it.complete(Clock.System.now()) + } + else -> it + } + } } } From 050f273e7b2a6475a688d25e190ebc854f3311ce Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 13:48:15 -0500 Subject: [PATCH 48/73] Rename RideViewModel.kt as Trip- --- .../org/pointyware/xyz/feature/ride/di/RideDependencies.kt | 6 +++--- .../kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt | 4 ++-- .../xyz/feature/ride/ui/PassengerDashboardScreen.kt | 4 ++-- .../ride/viewmodels/{RideViewModel.kt => TripViewModel.kt} | 2 +- .../ui/PassengerDashboardScreenUiTest.kt | 5 ++--- 5 files changed, 10 insertions(+), 11 deletions(-) rename feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/{RideViewModel.kt => TripViewModel.kt} (99%) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt index a2ea509..f5958e5 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideDependencies.kt @@ -6,18 +6,18 @@ package org.pointyware.xyz.feature.ride.di import org.koin.core.component.KoinComponent import org.koin.core.component.get -import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel +import org.pointyware.xyz.feature.ride.viewmodels.TripViewModel /** * */ interface RideDependencies { - fun getRideViewModel(): RideViewModel + fun getRideViewModel(): TripViewModel } class KoinRideDependencies: RideDependencies, KoinComponent { - override fun getRideViewModel(): RideViewModel { + override fun getRideViewModel(): TripViewModel { return get() } } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt index def3fec..9b1a05b 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/di/RideModule.kt @@ -21,7 +21,7 @@ import org.pointyware.xyz.feature.ride.data.TestTripRepository import org.pointyware.xyz.feature.ride.local.PaymentStore import org.pointyware.xyz.feature.ride.local.PaymentStoreImpl import org.pointyware.xyz.feature.ride.local.FakePaymentStore -import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel +import org.pointyware.xyz.feature.ride.viewmodels.TripViewModel /** * @@ -36,7 +36,7 @@ fun featureRideModule() = module { } fun featureRideViewModelModule() = module { - factoryOf(::RideViewModel) + factoryOf(::TripViewModel) } fun featureRideDataModule() = module { diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt index 7de9eaa..2f86d01 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt @@ -9,14 +9,14 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import org.pointyware.xyz.core.navigation.XyzNavController -import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel +import org.pointyware.xyz.feature.ride.viewmodels.TripViewModel /** * Displays a map with controls for starting, monitoring, and canceling a ride. */ @Composable fun PassengerDashboardScreen( - viewModel: RideViewModel, + viewModel: TripViewModel, navController: XyzNavController, ) { val state = viewModel.state.collectAsState() diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt similarity index 99% rename from feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt rename to feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index 0e82e3c..351d1dc 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/RideViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -24,7 +24,7 @@ import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState * * @see PassengerDashboardUiState */ -class RideViewModel( +class TripViewModel( private val tripRepository: TripRepository, private val paymentRepository: PaymentRepository ): MapViewModelImpl() { diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index a895dc9..67f66c6 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -35,7 +35,6 @@ import org.pointyware.xyz.core.navigation.di.homeQualifier import org.pointyware.xyz.core.ui.design.XyzTheme import org.pointyware.xyz.core.ui.di.EmptyTestUiDependencies import org.pointyware.xyz.feature.ride.data.TestTripRepository -import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.di.featureRideDataTestModule import org.pointyware.xyz.feature.ride.entities.ExpirationDate import org.pointyware.xyz.feature.ride.entities.PaymentMethod @@ -43,7 +42,7 @@ import org.pointyware.xyz.feature.ride.local.FakePaymentStore import org.pointyware.xyz.feature.ride.navigation.rideRoute import org.pointyware.xyz.feature.ride.test.setupKoin import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState -import org.pointyware.xyz.feature.ride.viewmodels.RideViewModel +import org.pointyware.xyz.feature.ride.viewmodels.TripViewModel import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -58,7 +57,7 @@ class PassengerDashboardScreenUiTest { private lateinit var tripRepository: TestTripRepository private lateinit var paymentStore: FakePaymentStore - private lateinit var viewModel: RideViewModel + private lateinit var viewModel: TripViewModel private lateinit var navController: XyzNavController private lateinit var driverProfile: DriverProfile From 8fddf733d9b329bc9735f1053ac5581ec85c38b6 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 16:25:34 -0500 Subject: [PATCH 49/73] Watch for accepted trip events --- .../commonMain/kotlin/BriefProfileUiState.kt | 12 ++++++ .../xyz/feature/ride/data/TripRepository.kt | 23 +++++++++++ .../feature/ride/viewmodels/TripViewModel.kt | 39 ++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt b/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt index 33aadd4..fe612c2 100644 --- a/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt +++ b/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt @@ -7,6 +7,7 @@ package org.pointyware.xyz.core.viewmodels import org.pointyware.xyz.core.entities.ride.Rating import org.pointyware.xyz.core.entities.data.Uri import org.pointyware.xyz.core.entities.Uuid +import org.pointyware.xyz.core.entities.profile.Profile /** * A brief profile UI state. For more detail see [ProfileUiState]. @@ -17,3 +18,14 @@ interface BriefProfileUiState { val name: String val rating: Rating } + +data class BriefProfileUiStateWrapper( + private val profile: Profile +): BriefProfileUiState { + override val id: Uuid = profile.id + override val image: Uri = profile.picture + override val name: String = profile.name.given + override val rating: Rating = Rating.FIVE // TODO: get from profile +} + +fun Profile.toBriefProfileUiState(): BriefProfileUiState = BriefProfileUiStateWrapper(this) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 4dcf61e..2b3bb5c 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -6,8 +6,11 @@ package org.pointyware.xyz.feature.ride.data import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.datetime.Clock @@ -24,6 +27,15 @@ import org.pointyware.xyz.core.entities.ride.Ride import org.pointyware.xyz.core.entities.ride.planRide import kotlin.time.Duration.Companion.milliseconds +sealed interface TripEvent { + data class Accepted( + val driverProfile: DriverProfile, + val pendingRide: PendingRide + ): TripEvent + data object PickedUp: TripEvent + data object DroppedOff: TripEvent +} + /** * Handles trip search and scheduling. */ @@ -33,6 +45,11 @@ interface TripRepository { */ val currentTrip: StateFlow + /** + * Events that occur during a trip. + */ + val tripEvents: SharedFlow + suspend fun searchDestinations(query: String): Result suspend fun findRoute(origin: Location, destination: Location): Result suspend fun requestRide(route: Route): Result @@ -49,6 +66,8 @@ class TripRepositoryImpl( override val currentTrip: StateFlow get() = TODO("Not yet implemented") + override val tripEvents: SharedFlow + get() = TODO("Not yet implemented") override suspend fun searchDestinations(query: String): Result { return service.searchDestinations(query) @@ -87,6 +106,10 @@ class TestTripRepository( override val currentTrip: StateFlow get() = mutableCurrentTrip.asStateFlow() + private val mutableTripEvents = MutableSharedFlow(replay = 1) + override val tripEvents: SharedFlow + get() = mutableTripEvents.asSharedFlow() + private val maximumLevenshteinDistance = 20 /** diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index 351d1dc..a1ab6ad 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -4,6 +4,7 @@ package org.pointyware.xyz.feature.ride.viewmodels +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update @@ -14,7 +15,9 @@ import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.viewmodels.LoadingUiState import org.pointyware.xyz.core.viewmodels.MapViewModelImpl import org.pointyware.xyz.core.viewmodels.postError +import org.pointyware.xyz.core.viewmodels.toBriefProfileUiState import org.pointyware.xyz.feature.ride.data.PaymentRepository +import org.pointyware.xyz.feature.ride.data.TripEvent import org.pointyware.xyz.feature.ride.data.TripRepository import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState @@ -117,14 +120,41 @@ class TripViewModel( PassengerDashboardUiState.Posted( route = route, price = price - ) - // TODO: listen for driver acceptance; update state + ).also { + watchDriverAcceptance() + } } else { it } } } + private var driverAcceptanceJob: Job? = null + private fun watchDriverAcceptance() { + driverAcceptanceJob?.cancel() + driverAcceptanceJob = viewModelScope.launch { + tripRepository.tripEvents.collect { event -> + when (event) { + is TripEvent.Accepted -> { + mutableState.update { + if (it is PassengerDashboardUiState.Posted) { +// val eta = event.pendingRide.route.eta // TODO: create eta use case + PassengerDashboardUiState.Waiting( + driver = event.driverProfile.toBriefProfileUiState(), + eta = 0, + route = it.route + ) + } else { + it + } + } + } + else -> {} + } + } + } + } + fun cancelRide() { // TODO: send cancellation request to server mutableState.update { @@ -163,4 +193,9 @@ class TripViewModel( } } } + + override fun dispose() { + super.dispose() + driverAcceptanceJob?.cancel() + } } From ecc1edb264850d588c11c0be5e8fcdfa4dab7913 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 16:29:31 -0500 Subject: [PATCH 50/73] Emit trip events --- .../xyz/feature/ride/data/TripRepository.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 2b3bb5c..040191f 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -106,7 +106,7 @@ class TestTripRepository( override val currentTrip: StateFlow get() = mutableCurrentTrip.asStateFlow() - private val mutableTripEvents = MutableSharedFlow(replay = 1) + private val mutableTripEvents = MutableSharedFlow() override val tripEvents: SharedFlow get() = mutableTripEvents.asSharedFlow() @@ -193,7 +193,9 @@ class TestTripRepository( mutableCurrentTrip.update { when (it) { is PlannedRide -> { - it.accept(driverProfile, Clock.System.now()) + it.accept(driverProfile, Clock.System.now()).also { + mutableTripEvents.tryEmit(TripEvent.Accepted(driverProfile, it)) + } } else -> it } @@ -204,7 +206,9 @@ class TestTripRepository( mutableCurrentTrip.update { when (it) { is PendingRide -> { - it.arrive(Clock.System.now()) + it.arrive(Clock.System.now()).also { + mutableTripEvents.tryEmit(TripEvent.PickedUp) + } } else -> it } @@ -215,7 +219,9 @@ class TestTripRepository( mutableCurrentTrip.update { when (it) { is ActiveRide -> { - it.complete(Clock.System.now()) + it.complete(Clock.System.now()).also { + mutableTripEvents.tryEmit(TripEvent.DroppedOff) + } } else -> it } From c2346c5173fef25a84686ef1eef7979652dc9417 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 17:56:02 -0500 Subject: [PATCH 51/73] Move MessageInput to :core:ui --- core/ui/src/commonMain/kotlin/MessageInput.kt | 25 +++++++++++++++++++ .../kotlin/ui/ProviderDashboardScreen.kt | 12 +-------- 2 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 core/ui/src/commonMain/kotlin/MessageInput.kt diff --git a/core/ui/src/commonMain/kotlin/MessageInput.kt b/core/ui/src/commonMain/kotlin/MessageInput.kt new file mode 100644 index 0000000..335d64f --- /dev/null +++ b/core/ui/src/commonMain/kotlin/MessageInput.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Pointyware. Use of this software is governed by the GPL-3.0 license. + */ + +package org.pointyware.xyz.core.ui + +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics + +/** + * + */ +@Composable +fun MessageInput( + modifier: Modifier = Modifier, +) { + TextField( + value = "", + onValueChange = {}, + modifier = modifier.semantics { contentDescription = "Message Input" } + ) +} diff --git a/feature/drive/src/commonMain/kotlin/ui/ProviderDashboardScreen.kt b/feature/drive/src/commonMain/kotlin/ui/ProviderDashboardScreen.kt index 4b22d10..b7f5f59 100644 --- a/feature/drive/src/commonMain/kotlin/ui/ProviderDashboardScreen.kt +++ b/feature/drive/src/commonMain/kotlin/ui/ProviderDashboardScreen.kt @@ -26,6 +26,7 @@ import org.pointyware.xyz.core.navigation.XyzNavController import org.pointyware.xyz.core.ui.AdView import org.pointyware.xyz.core.ui.AdViewState import org.pointyware.xyz.core.ui.MapView +import org.pointyware.xyz.core.ui.MessageInput import org.pointyware.xyz.core.viewmodels.MapUiState import org.pointyware.xyz.drive.viewmodels.ProviderDashboardViewModel import org.pointyware.xyz.drive.viewmodels.RideRequestUiState @@ -176,17 +177,6 @@ fun DeliveryInfo( } } -@Composable -fun MessageInput( - modifier: Modifier = Modifier, -) { - TextField( - value = "", - onValueChange = {}, - modifier = modifier.semantics { contentDescription = "Message Input" } - ) -} - @Composable fun TripCompletionView( onConfirmCompletion: () -> Unit, From 49cfd78e902c57898defbb9ee88aafc21fb6e0e5 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:03:07 -0500 Subject: [PATCH 52/73] Review Drive.status forms --- .../src/commonMain/kotlin/ride/Ride.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/entities/src/commonMain/kotlin/ride/Ride.kt b/core/entities/src/commonMain/kotlin/ride/Ride.kt index d6d26a8..3a403c2 100644 --- a/core/entities/src/commonMain/kotlin/ride/Ride.kt +++ b/core/entities/src/commonMain/kotlin/ride/Ride.kt @@ -24,7 +24,7 @@ sealed interface Ride { /** * The current status of the ride as it progresses through the system. */ - val status: Status + val status: Status // TODO: remove; redundant /** * The time the ride was posted to the system by the rider. @@ -110,6 +110,14 @@ sealed interface Ride { val timeToStart: Instant ): Status, RouteProgress.Unrealized + /** + * The ride has been accepted by a driver for future completion. + * Possible transitions are [Active], [Ended] + */ + data class AcceptedImmediate( + val timeAccepted: Instant + ): Status, RouteProgress.Unrealized + /** * The ride is in progress. * Possible transitions are [Ended] @@ -242,7 +250,7 @@ data class PendingRide( ): Ride { override val status: Ride.Status - get() = Ride.Status.Immediate + get() = Ride.Status.AcceptedImmediate(timeAccepted) fun arrive(timeArrived: Instant): ActiveRide { return ActiveRide( @@ -271,7 +279,7 @@ data class ActiveRide( ): Ride { override val status: Ride.Status - get() = Ride.Status.Active(TODO()) + get() = Ride.Status.Active(plannedRoute) fun start(timeStarted: Instant): CompletingRide { return CompletingRide( @@ -320,7 +328,7 @@ data class CompletingRide( ): Ride { override val status: Ride.Status - get() = Ride.Status.Active(TODO()) + get() = Ride.Status.Active(plannedRoute) fun complete(timeEnded: Instant): CompletedRide { return CompletedRide( @@ -349,5 +357,5 @@ data class CompletedRide( override val timeEnded: Instant, ): Ride { override val status: Ride.Status - get() = Ride.Status.Completed(TODO()) + get() = Ride.Status.Completed(plannedRoute) } From 56c27ac7f6906c34b6fd789ffac936fb43eb526d Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:05:44 -0500 Subject: [PATCH 53/73] reformat actions/expectations for clarity --- .../ui/PassengerDashboardScreenUiTest.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 67f66c6..11b048b 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -138,7 +138,6 @@ class PassengerDashboardScreenUiTest { } } - onNodeWithText("New Ride") .assertExists() .assertIsEnabled() @@ -168,7 +167,6 @@ class PassengerDashboardScreenUiTest { Then: - The "Payment Selection" button transforms into the Payment Method Selection */ - onNodeWithText("Select Payment Method") .performClick() @@ -201,6 +199,7 @@ class PassengerDashboardScreenUiTest { */ onNodeWithText("Search") .performTextInput("Red Rock") + onNodeWithText("Search") .assert(hasText("Red Rock")) onNodeWithText("Confirm") @@ -214,6 +213,7 @@ class PassengerDashboardScreenUiTest { */ onNodeWithText("Confirm") .performClick() + onNodeWithContentDescription("Location Suggestions") .assertExists() @@ -229,6 +229,7 @@ class PassengerDashboardScreenUiTest { .onChildren().filterToOne(hasText("Red Rock", substring = true)) .assertExists() .performClick() + // TODO: Assert that the map is updated onNodeWithContentDescription("Loading") .assertExists() @@ -236,8 +237,6 @@ class PassengerDashboardScreenUiTest { .assertExists() .assertIsNotEnabled() - waitUntilDoesNotExist(hasContentDescription("Loading"), 2000L) - /* When: - The route is calculated @@ -245,6 +244,8 @@ class PassengerDashboardScreenUiTest { - The "Confirm Route" button is shown - The waiting indicator is no longer shown */ + waitUntilDoesNotExist(hasContentDescription("Loading"), 2000L) + onNodeWithText("Confirm Route") .assertExists() .assertIsEnabled() @@ -259,6 +260,7 @@ class PassengerDashboardScreenUiTest { */ onNodeWithText("Confirm Route") .performClick() + onNodeWithText("Cancel Request") .assertExists() .assertIsEnabled() From 55f012a56637ca8a4f80f8b5c39e87cffba7afa8 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:32:07 -0500 Subject: [PATCH 54/73] Add hailing message expectation --- .../ui/PassengerDashboardScreenUiTest.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 11b048b..158e5ef 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -18,6 +18,8 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.runComposeUiTest import androidx.compose.ui.test.waitUntilDoesNotExist +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin import org.koin.dsl.module @@ -256,11 +258,14 @@ class PassengerDashboardScreenUiTest { When: - User clicks on the "Confirm Route" button Then: + - The "Hailing a driver" message is shown - The "Cancel Request" button is shown */ onNodeWithText("Confirm Route") .performClick() + onNodeWithText("Hailing a driver") + .assertExists() onNodeWithText("Cancel Request") .assertExists() .assertIsEnabled() @@ -275,6 +280,7 @@ class PassengerDashboardScreenUiTest { */ tripRepository.acceptRequest(driverProfile) + runBlocking { delay(100) } onNodeWithContentDescription("Driver Profile") .assertExists() onNodeWithContentDescription("Message Input") From cf0d56c0184f8abd782ecc67c8191e3e604582d6 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:32:37 -0500 Subject: [PATCH 55/73] Emit event in coroutine scope to ensure emission --- .../org/pointyware/xyz/feature/ride/data/TripRepository.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 040191f..24bd58e 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlinx.datetime.Instant import org.pointyware.xyz.core.entities.Uuid @@ -194,7 +195,9 @@ class TestTripRepository( when (it) { is PlannedRide -> { it.accept(driverProfile, Clock.System.now()).also { - mutableTripEvents.tryEmit(TripEvent.Accepted(driverProfile, it)) + dataScope.launch { + mutableTripEvents.emit(TripEvent.Accepted(driverProfile, it)) + } } } else -> it From f6c62e34fdabc392cd0bf31d48de3efe9fd8ba7a Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:33:06 -0500 Subject: [PATCH 56/73] Add driver profile content description and message input --- .../xyz/feature/ride/ui/TripSearchView.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index 151cf21..815d9e0 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -29,9 +29,9 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import org.pointyware.xyz.core.entities.geo.Location +import org.pointyware.xyz.core.ui.MessageInput import org.pointyware.xyz.feature.ride.entities.PaymentMethod import org.pointyware.xyz.feature.ride.viewmodels.PassengerDashboardUiState -import org.pointyware.xyz.feature.ride.ui.PaymentSelectionView data class TripSearchViewState( val isExpanded: Boolean, @@ -216,6 +216,7 @@ fun PostedRideView( onCancelRequest: ()->Unit ) { Column { + // TODO: display route/rate details Text("Hailing a driver") Button(onClick = onCancelRequest) { Text("Cancel Request") @@ -227,8 +228,15 @@ fun PostedRideView( fun AwaitingRideView( state: PassengerDashboardUiState.Waiting ) { - Text("Waiting for driver") - // TODO: rider details + Column( + modifier = Modifier.semantics { + contentDescription = "Driver Profile" + } + ) { + Text("Driver: ${state.driver.name}") + Text("ETA: ${state.eta}") + MessageInput() + } } @Composable From 1a899e7a03fd8f423b41024ab5ce5a94071f505d Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:33:28 -0500 Subject: [PATCH 57/73] Send trip request on detail confirmation --- .../feature/ride/viewmodels/TripViewModel.kt | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index a1ab6ad..c10c83b 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -113,18 +113,25 @@ class TripViewModel( } fun confirmDetails() { - mutableState.update { - if (it is PassengerDashboardUiState.Confirm) { - val route = it.route ?: return - val price = it.price ?: return - PassengerDashboardUiState.Posted( - route = route, - price = price - ).also { - watchDriverAcceptance() + viewModelScope.launch { + mutableState.update { uiState -> + if (uiState is PassengerDashboardUiState.Confirm) { + val route = uiState.route ?: return@launch + val price = uiState.price ?: return@launch + tripRepository.requestRide(uiState.route) + .onFailure { + it.printStackTrace() + return@launch + } + PassengerDashboardUiState.Posted( + route = route, + price = price + ).also { + watchDriverAcceptance() + } + } else { + uiState } - } else { - it } } } From d3d75624e0db8b827469c881ba0b35ac07472735 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:35:52 -0500 Subject: [PATCH 58/73] replace arbitrary delay with smart wait --- .../ui/PassengerDashboardScreenUiTest.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 158e5ef..6ea0fbc 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -18,8 +18,7 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.runComposeUiTest import androidx.compose.ui.test.waitUntilDoesNotExist -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking +import androidx.compose.ui.test.waitUntilExactlyOneExists import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin import org.koin.dsl.module @@ -280,7 +279,7 @@ class PassengerDashboardScreenUiTest { */ tripRepository.acceptRequest(driverProfile) - runBlocking { delay(100) } + waitUntilExactlyOneExists(hasContentDescription("Driver Profile"), 500L) onNodeWithContentDescription("Driver Profile") .assertExists() onNodeWithContentDescription("Message Input") From 7116d59c8e1a44a28e08a53c62cc3267a7134b54 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:40:33 -0500 Subject: [PATCH 59/73] Add user readable name to Accommodation --- core/entities/src/commonMain/kotlin/ride/Accommodation.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/entities/src/commonMain/kotlin/ride/Accommodation.kt b/core/entities/src/commonMain/kotlin/ride/Accommodation.kt index 51b664e..c22322e 100644 --- a/core/entities/src/commonMain/kotlin/ride/Accommodation.kt +++ b/core/entities/src/commonMain/kotlin/ride/Accommodation.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable * Describes a driver accommodation that can be provided to a rider with a [Disability]. */ @Serializable -sealed class Accommodation { - data object WheelchairAccess : Accommodation() - data object AnimalFriendly : Accommodation() +sealed class Accommodation(val name: String) { // TODO: replace with resource ID for i18n + data object WheelchairAccess : Accommodation("Wheelchair Access") + data object AnimalFriendly : Accommodation("Animal Friendly") } From 426cc362823deb5aba1ce2f3e3fefac493745682 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:40:50 -0500 Subject: [PATCH 60/73] replace BriefProfileUiState with DriverProfile --- .../src/commonMain/kotlin/BriefProfileUiState.kt | 11 ----------- .../ride/viewmodels/PassengerDashboardUiState.kt | 10 +++++----- .../xyz/feature/ride/viewmodels/TripViewModel.kt | 3 +-- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt b/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt index fe612c2..b7b7bc1 100644 --- a/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt +++ b/core/view-models/src/commonMain/kotlin/BriefProfileUiState.kt @@ -18,14 +18,3 @@ interface BriefProfileUiState { val name: String val rating: Rating } - -data class BriefProfileUiStateWrapper( - private val profile: Profile -): BriefProfileUiState { - override val id: Uuid = profile.id - override val image: Uri = profile.picture - override val name: String = profile.name.given - override val rating: Rating = Rating.FIVE // TODO: get from profile -} - -fun Profile.toBriefProfileUiState(): BriefProfileUiState = BriefProfileUiStateWrapper(this) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt index fa36eab..d06318e 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/PassengerDashboardUiState.kt @@ -7,7 +7,7 @@ package org.pointyware.xyz.feature.ride.viewmodels import org.pointyware.xyz.core.entities.business.Currency import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.entities.geo.Route -import org.pointyware.xyz.core.viewmodels.BriefProfileUiState +import org.pointyware.xyz.core.entities.profile.DriverProfile import org.pointyware.xyz.feature.ride.ui.PaymentSelectionViewState /** @@ -52,9 +52,9 @@ sealed interface PassengerDashboardUiState { */ data class Waiting( /** - * The driver's name. + * The driver's information. */ - val driver: BriefProfileUiState, + val driver: DriverProfile, /** * Driver's estimated time of arrival in minutes. */ @@ -66,7 +66,7 @@ sealed interface PassengerDashboardUiState { * The rider is in the car and the ride is in progress. */ data class Riding( - val driver: BriefProfileUiState, + val driver: DriverProfile, val route: Route, val eta: Int ): PassengerDashboardUiState @@ -75,7 +75,7 @@ sealed interface PassengerDashboardUiState { * The rider has arrived at the destination. */ data class Arrived( - val driver: BriefProfileUiState, + val driver: DriverProfile, val route: Route ): PassengerDashboardUiState } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index c10c83b..3fa30b6 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -15,7 +15,6 @@ import org.pointyware.xyz.core.entities.geo.Location import org.pointyware.xyz.core.viewmodels.LoadingUiState import org.pointyware.xyz.core.viewmodels.MapViewModelImpl import org.pointyware.xyz.core.viewmodels.postError -import org.pointyware.xyz.core.viewmodels.toBriefProfileUiState import org.pointyware.xyz.feature.ride.data.PaymentRepository import org.pointyware.xyz.feature.ride.data.TripEvent import org.pointyware.xyz.feature.ride.data.TripRepository @@ -147,7 +146,7 @@ class TripViewModel( if (it is PassengerDashboardUiState.Posted) { // val eta = event.pendingRide.route.eta // TODO: create eta use case PassengerDashboardUiState.Waiting( - driver = event.driverProfile.toBriefProfileUiState(), + driver = event.driverProfile, eta = 0, route = it.route ) From 31d4860f6e528f2f9a5c43a863c6a0d5c083b9eb Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:43:31 -0500 Subject: [PATCH 61/73] Fix wrong text --- .../org/pointyware/xyz/feature/ride/ui/TripSearchView.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index 815d9e0..cb5f743 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -233,8 +233,10 @@ fun AwaitingRideView( contentDescription = "Driver Profile" } ) { - Text("Driver: ${state.driver.name}") - Text("ETA: ${state.eta}") + Text("Driver is on the way") + state.driver.accommodations.forEach { + Text(text = it.name) + } MessageInput() } } From 652491d8536ed0127b31697a4bffe8a2fc7babfe Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:45:57 -0500 Subject: [PATCH 62/73] Add missing elements from active ride view --- .../xyz/feature/ride/ui/TripSearchView.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index cb5f743..3d08075 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -107,7 +107,10 @@ fun TripSearchView( } is PassengerDashboardUiState.Riding -> { - ActiveRideView(state = state) + ActiveRideView( + state = state, + onCancelTrip = onCancelRequest + ) } is PassengerDashboardUiState.Arrived -> { @@ -243,10 +246,18 @@ fun AwaitingRideView( @Composable fun ActiveRideView( - state: PassengerDashboardUiState.Riding + state: PassengerDashboardUiState.Riding, + onCancelTrip: ()->Unit ) { - // Do nothing - // TODO: rider details + Column { + Text("Riding with ${state.driver.name}") + Text("ETA: ${state.eta}") + + Button(onClick = onCancelTrip) { + Text("Cancel Ride") + } + MessageInput() + } } @Composable From e7017ce6a0fd537f82a84af100121ad6bb4bdd4b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:51:23 -0500 Subject: [PATCH 63/73] Add smart wait for button --- .../ui/PassengerDashboardScreenUiTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index 6ea0fbc..d09fb23 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -300,6 +300,7 @@ class PassengerDashboardScreenUiTest { */ tripRepository.pickUpRider() + waitUntilExactlyOneExists(hasText("Cancel Ride"), 500L) onNodeWithText("Cancel Ride") .assertExists() .assertIsEnabled() From 1b25efbceecae5567132673ef80b3c851870f023 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:51:41 -0500 Subject: [PATCH 64/73] Include profile and ride in each trip event --- .../xyz/feature/ride/data/TripRepository.kt | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index 24bd58e..f2f8050 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -22,6 +22,7 @@ import org.pointyware.xyz.core.entities.geo.Route import org.pointyware.xyz.core.entities.profile.DriverProfile import org.pointyware.xyz.core.entities.profile.RiderProfile import org.pointyware.xyz.core.entities.ride.ActiveRide +import org.pointyware.xyz.core.entities.ride.CompletedRide import org.pointyware.xyz.core.entities.ride.PendingRide import org.pointyware.xyz.core.entities.ride.PlannedRide import org.pointyware.xyz.core.entities.ride.Ride @@ -29,12 +30,21 @@ import org.pointyware.xyz.core.entities.ride.planRide import kotlin.time.Duration.Companion.milliseconds sealed interface TripEvent { + val driverProfile: DriverProfile + val ride: Ride + data class Accepted( - val driverProfile: DriverProfile, - val pendingRide: PendingRide + override val driverProfile: DriverProfile, + override val ride: PendingRide + ): TripEvent + data class PickedUp( + override val driverProfile: DriverProfile, + override val ride: ActiveRide + ): TripEvent + data class DroppedOff( + override val driverProfile: DriverProfile, + override val ride: CompletedRide ): TripEvent - data object PickedUp: TripEvent - data object DroppedOff: TripEvent } /** @@ -210,7 +220,7 @@ class TestTripRepository( when (it) { is PendingRide -> { it.arrive(Clock.System.now()).also { - mutableTripEvents.tryEmit(TripEvent.PickedUp) + mutableTripEvents.tryEmit(TripEvent.PickedUp(it.driver, it)) } } else -> it @@ -223,7 +233,7 @@ class TestTripRepository( when (it) { is ActiveRide -> { it.complete(Clock.System.now()).also { - mutableTripEvents.tryEmit(TripEvent.DroppedOff) + mutableTripEvents.tryEmit(TripEvent.DroppedOff(it.driver, it)) } } else -> it From 27b7dcbc461c301a6640235eeb08881c279e3296 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 18:51:49 -0500 Subject: [PATCH 65/73] cover remaining trip events --- .../feature/ride/viewmodels/TripViewModel.kt | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index 3fa30b6..6db346c 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -155,7 +155,32 @@ class TripViewModel( } } } - else -> {} + is TripEvent.PickedUp -> { + mutableState.update { + if (it is PassengerDashboardUiState.Waiting) { +// val eta = event.pendingRide.route.eta // TODO: create eta use case + PassengerDashboardUiState.Riding( + driver = event.driverProfile, + eta = 0, + route = it.route + ) + } else { + it + } + } + } + is TripEvent.DroppedOff -> { + mutableState.update { + if (it is PassengerDashboardUiState.Riding) { + PassengerDashboardUiState.Arrived( + driver = event.driverProfile, + route = it.route + ) + } else { + it + } + } + } } } } From 9de966984b482b1e42d804aa9a4c40a1d10ffc3b Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:06:07 -0500 Subject: [PATCH 66/73] Fill completed ride view --- .../org/pointyware/xyz/feature/ride/ui/TripSearchView.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index 3d08075..7792779 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -219,7 +219,6 @@ fun PostedRideView( onCancelRequest: ()->Unit ) { Column { - // TODO: display route/rate details Text("Hailing a driver") Button(onClick = onCancelRequest) { Text("Cancel Request") @@ -265,5 +264,8 @@ fun CompletedRideView( state: PassengerDashboardUiState.Arrived, modifier: Modifier = Modifier, ) { - + Column { + Text("Arrived at destination") + Text("Thanks for riding with us!") + } } From e3bdfd24e0c66611c5de62a2bc259eace80f4485 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:09:44 -0500 Subject: [PATCH 67/73] wrap event emissions in coroutines --- .../pointyware/xyz/feature/ride/data/TripRepository.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt index f2f8050..92c0051 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/data/TripRepository.kt @@ -220,7 +220,9 @@ class TestTripRepository( when (it) { is PendingRide -> { it.arrive(Clock.System.now()).also { - mutableTripEvents.tryEmit(TripEvent.PickedUp(it.driver, it)) + dataScope.launch { + mutableTripEvents.emit(TripEvent.PickedUp(it.driver, it)) + } } } else -> it @@ -233,7 +235,9 @@ class TestTripRepository( when (it) { is ActiveRide -> { it.complete(Clock.System.now()).also { - mutableTripEvents.tryEmit(TripEvent.DroppedOff(it.driver, it)) + dataScope.launch { + mutableTripEvents.emit(TripEvent.DroppedOff(it.driver, it)) + } } } else -> it From 595b6bea77a27dd9c979b12e433d1d68c75dae4c Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:13:02 -0500 Subject: [PATCH 68/73] add driver profile semantics; remove erroneous text --- .../pointyware/xyz/feature/ride/ui/TripSearchView.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index 7792779..d249349 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -248,10 +248,12 @@ fun ActiveRideView( state: PassengerDashboardUiState.Riding, onCancelTrip: ()->Unit ) { - Column { - Text("Riding with ${state.driver.name}") - Text("ETA: ${state.eta}") - + Column( + modifier = Modifier.semantics { + contentDescription = "Driver Profile" + } + ) { + Text(text = "You're on your way!") Button(onClick = onCancelTrip) { Text("Cancel Ride") } From 8cb4136eeb0b970fa0b468ad386e52f04768eaaf Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:23:37 -0500 Subject: [PATCH 69/73] Add cancel/rate view model events and bindings --- .../ride/ui/PassengerDashboardScreen.kt | 21 +++++++++-------- .../feature/ride/ui/PassengerDashboardView.kt | 10 ++++++-- .../xyz/feature/ride/ui/TripSearchView.kt | 23 +++++++++++++++---- .../feature/ride/viewmodels/TripViewModel.kt | 18 +++++++++++++++ 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt index 2f86d01..5a53bc3 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardScreen.kt @@ -28,15 +28,18 @@ fun PassengerDashboardScreen( state = rideViewState, loadingState = loadingState.value, modifier = Modifier.fillMaxSize(), - onStartSearch = { viewModel.startSearch() }, - onUpdateQuery = { viewModel.updateQuery(it) }, - onSendQuery = { viewModel.sendQuery() }, - onSelectLocation = { viewModel.selectLocation(it) }, - onConfirmDetails = { viewModel.confirmDetails() }, - onCancel = { viewModel.cancelRide() }, - onBack = { navController.goBack() }, - clearError = { viewModel.clearError() }, + onStartSearch = viewModel::startSearch, + onUpdateQuery = viewModel::updateQuery, + onSendQuery = viewModel::sendQuery, + onSelectLocation = viewModel::selectLocation, + onConfirmDetails = viewModel::confirmDetails, + onCancel = viewModel::cancelRide, onSelectPayment = viewModel::onSelectPayment, - onPaymentSelected = viewModel::onPaymentSelected + onPaymentSelected = viewModel::onPaymentSelected, + onCancelTrip = viewModel::onCancelTrip, + onRateDriver = viewModel::onRateDriver, + onFinishTrip = viewModel::onFinishTrip, + onBack = navController::goBack, + clearError = viewModel::clearError, ) } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt index bbe3058..98aae14 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/PassengerDashboardView.kt @@ -40,6 +40,9 @@ fun PassengerDashboardView( onPaymentSelected: (PaymentMethod)->Unit, onConfirmDetails: ()->Unit, onCancel: ()->Unit, + onCancelTrip: ()->Unit, + onFinishTrip: ()->Unit, + onRateDriver: ()->Unit, onBack: ()->Unit, clearError: ()->Unit ) { @@ -70,9 +73,12 @@ fun PassengerDashboardView( onSendQuery = onSendQuery, onSelectLocation = onSelectLocation, onConfirmDetails = onConfirmDetails, - onCancelRequest = onCancel, onSelectPayment = onSelectPayment, - onPaymentSelected = onPaymentSelected + onPaymentSelected = onPaymentSelected, + onCancelRequest = onCancel, + onCancelTrip = onCancelTrip, + onFinishTrip = onFinishTrip, + onRateDriver = onRateDriver, ) } } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index d249349..ac88612 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -51,9 +51,12 @@ fun TripSearchView( onSendQuery: ()->Unit, onSelectLocation: (Location)->Unit, onConfirmDetails: ()->Unit, - onCancelRequest: ()->Unit, onSelectPayment: () -> Unit, - onPaymentSelected: (PaymentMethod) -> Unit + onPaymentSelected: (PaymentMethod) -> Unit, + onCancelRequest: ()->Unit, + onCancelTrip: ()->Unit, + onRateDriver: () -> Unit, + onFinishTrip: ()->Unit, ) { val shape = when (state) { is PassengerDashboardUiState.Idle, @@ -114,7 +117,10 @@ fun TripSearchView( } is PassengerDashboardUiState.Arrived -> { - CompletedRideView(state = state) + CompletedRideView( + state = state, + onRateDriver = onRateDriver + ) } } } @@ -265,9 +271,18 @@ fun ActiveRideView( fun CompletedRideView( state: PassengerDashboardUiState.Arrived, modifier: Modifier = Modifier, + onRateDriver: ()->Unit ) { - Column { + Column( + modifier = modifier + ) { Text("Arrived at destination") Text("Thanks for riding with us!") + + Button( + onClick = onRateDriver, + ) { + Text("Rate Driver") + } } } diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt index 6db346c..e915cba 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/viewmodels/TripViewModel.kt @@ -225,6 +225,24 @@ class TripViewModel( } } + fun onCancelTrip() { + TODO("Not yet implemented") + } + + fun onRateDriver() { + TODO("Not yet implemented") + } + + fun onFinishTrip() { + mutableState.update { + if (it is PassengerDashboardUiState.Arrived) { + PassengerDashboardUiState.Idle + } else { + it + } + } + } + override fun dispose() { super.dispose() driverAcceptanceJob?.cancel() From 5339eb47ab6651836f53213a289d3d1e2efd3511 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:28:08 -0500 Subject: [PATCH 70/73] Add smart wait --- .../ui/PassengerDashboardScreenUiTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt index d09fb23..bd8fcd8 100644 --- a/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt +++ b/feature/ride/src/commonTest/kotlin/org.pointyware.xyz.feature.ride/ui/PassengerDashboardScreenUiTest.kt @@ -323,6 +323,7 @@ class PassengerDashboardScreenUiTest { */ tripRepository.dropOffRider() + waitUntilExactlyOneExists(hasText("Rate Driver"), 500L) onNodeWithText("Rate Driver") .assertExists() .assertIsEnabled() From 44db348a6074a9f2a74c4bd553f5fc69a2d55381 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:28:17 -0500 Subject: [PATCH 71/73] Fix arrival message --- .../kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index ac88612..ff933db 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -276,7 +276,7 @@ fun CompletedRideView( Column( modifier = modifier ) { - Text("Arrived at destination") + Text("You've arrived!") Text("Thanks for riding with us!") Button( From b37164c83a91e9ab52e7fb5f18f40cbcf5d6598a Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:29:49 -0500 Subject: [PATCH 72/73] Add missing done button --- .../pointyware/xyz/feature/ride/ui/TripSearchView.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt index ff933db..6b584fe 100644 --- a/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt +++ b/feature/ride/src/commonMain/kotlin/org/pointyware/xyz/feature/ride/ui/TripSearchView.kt @@ -119,7 +119,8 @@ fun TripSearchView( is PassengerDashboardUiState.Arrived -> { CompletedRideView( state = state, - onRateDriver = onRateDriver + onRateDriver = onRateDriver, + onFinishTrip = onFinishTrip ) } } @@ -271,7 +272,8 @@ fun ActiveRideView( fun CompletedRideView( state: PassengerDashboardUiState.Arrived, modifier: Modifier = Modifier, - onRateDriver: ()->Unit + onRateDriver: ()->Unit, + onFinishTrip: ()->Unit, ) { Column( modifier = modifier @@ -284,5 +286,10 @@ fun CompletedRideView( ) { Text("Rate Driver") } + Button( + onClick = onFinishTrip, + ) { + Text("Done") + } } } From c3420ccf749388ea419349c55973e460c9da8283 Mon Sep 17 00:00:00 2001 From: Taush Sampley Date: Mon, 23 Sep 2024 19:48:40 -0500 Subject: [PATCH 73/73] replace irrelevant waitForIdle with smart wait --- .../ui/ProviderDashboardScreenUiTest.kt | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/ui/ProviderDashboardScreenUiTest.kt b/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/ui/ProviderDashboardScreenUiTest.kt index 7df83cf..e7fce96 100644 --- a/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/ui/ProviderDashboardScreenUiTest.kt +++ b/feature/drive/src/commonTest/kotlin/org.pointyware.xyz.feature.drive/ui/ProviderDashboardScreenUiTest.kt @@ -7,13 +7,14 @@ package org.pointyware.xyz.feature.drive.ui import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsEnabled import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.onChild -import androidx.compose.ui.test.onChildren -import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.test.waitUntilDoesNotExist +import androidx.compose.ui.test.waitUntilExactlyOneExists import kotlinx.datetime.Clock import org.koin.core.context.loadKoinModules import org.koin.core.context.stopKoin @@ -165,11 +166,7 @@ class ProviderDashboardScreenUiTest { */ rideRepository.addRequest(testRequest) - waitForIdle() - onNodeWithContentDescription("Ride Requests") - .onChildren().onFirst().assertExists() - onNodeWithText("John") - .assertExists() + waitUntilExactlyOneExists(hasText("John")) onNodeWithText("Walmart") .assertExists() onNodeWithText("Walgreens") @@ -231,9 +228,7 @@ class ProviderDashboardScreenUiTest { onNodeWithText("Pick Up") .performClick() - waitForIdle() - onNodeWithText("Pick Up") - .assertDoesNotExist() + waitUntilDoesNotExist(hasText("Pick Up")) onNodeWithContentDescription("Rider Profile") .assertExists() onNodeWithContentDescription("Message Input") @@ -253,6 +248,7 @@ class ProviderDashboardScreenUiTest { */ locationService.setLocation(LatLong(36.1162121,-97.0583766)) + waitUntilExactlyOneExists(hasText("Drop Off")) onNodeWithText("Drop Off") .assertIsEnabled() @@ -331,11 +327,8 @@ class ProviderDashboardScreenUiTest { - The request has a accept/reject buttons */ rideRepository.addRequest(testRequest) - waitForIdle() - onNodeWithContentDescription("Ride Requests") - .onChildren().onFirst().assertExists() - onNodeWithText("John") - .assertExists() + + waitUntilExactlyOneExists(hasText("John")) onNodeWithText("Walmart") .assertExists() onNodeWithText("Walgreens")