Skip to content

Commit bf3935b

Browse files
Show progress indicator when cancelling the booking
1 parent 1916d68 commit bf3935b

File tree

6 files changed

+55
-15
lines changed

6 files changed

+55
-15
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingMapper.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.woocommerce.android.ui.bookings.compose.BookingCustomerDetailsModel
1010
import com.woocommerce.android.ui.bookings.compose.BookingPaymentDetailsModel
1111
import com.woocommerce.android.ui.bookings.compose.BookingStatus
1212
import com.woocommerce.android.ui.bookings.compose.BookingSummaryModel
13+
import com.woocommerce.android.ui.bookings.details.CancelState
1314
import com.woocommerce.android.ui.bookings.list.BookingListItem
1415
import com.woocommerce.android.util.CurrencyFormatter
1516
import com.woocommerce.android.viewmodel.ResourceProvider
@@ -56,7 +57,7 @@ class BookingMapper @Inject constructor(
5657
)
5758
}
5859

59-
fun Booking.toAppointmentDetailsModel(): BookingAppointmentDetailsModel {
60+
fun Booking.toAppointmentDetailsModel(cancelState: CancelState): BookingAppointmentDetailsModel {
6061
val durationMinutes = Duration.between(start, end).toMinutes()
6162
return BookingAppointmentDetailsModel(
6263
date = detailsDateFormatter.format(start),
@@ -65,7 +66,8 @@ class BookingMapper @Inject constructor(
6566
staff = "Marianne Renoir",
6667
location = "238 Willow Creek Drive, Montgomery AL 36109",
6768
duration = "$durationMinutes min",
68-
price = currencyFormatter.formatCurrency(cost, currency)
69+
price = currencyFormatter.formatCurrency(cost, currency),
70+
cancelState = cancelState,
6971
)
7072
}
7173

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/compose/BookingAppointmentDetails.kt

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.Row
88
import androidx.compose.foundation.layout.fillMaxWidth
99
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.size
1011
import androidx.compose.material3.ButtonDefaults
12+
import androidx.compose.material3.CircularProgressIndicator
1113
import androidx.compose.material3.HorizontalDivider
14+
import androidx.compose.material3.LocalContentColor
1215
import androidx.compose.material3.MaterialTheme
1316
import androidx.compose.material3.Text
1417
import androidx.compose.runtime.Composable
@@ -17,6 +20,7 @@ import androidx.compose.ui.res.stringResource
1720
import androidx.compose.ui.text.style.TextOverflow
1821
import androidx.compose.ui.unit.dp
1922
import com.woocommerce.android.R
23+
import com.woocommerce.android.ui.bookings.details.CancelState
2024
import com.woocommerce.android.ui.compose.component.WCOutlinedButton
2125
import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews
2226
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
@@ -62,9 +66,17 @@ fun BookingAppointmentDetails(
6266
colors = ButtonDefaults.outlinedButtonColors(
6367
contentColor = MaterialTheme.colorScheme.onSurface
6468
),
65-
onClick = onCancelBooking
69+
onClick = onCancelBooking,
70+
enabled = model.cancelButtonEnabled,
6671
) {
67-
Text(text = stringResource(R.string.booking_details_cancel_booking_button))
72+
if (model.cancelInProgressShown) {
73+
CircularProgressIndicator(
74+
color = LocalContentColor.current,
75+
modifier = Modifier.size(24.dp)
76+
)
77+
} else {
78+
Text(text = stringResource(R.string.booking_details_cancel_booking_button))
79+
}
6880
}
6981
HorizontalDivider(thickness = 0.5.dp)
7082
}
@@ -103,8 +115,12 @@ data class BookingAppointmentDetailsModel(
103115
val staff: String,
104116
val location: String,
105117
val duration: String,
106-
val price: String
107-
)
118+
val price: String,
119+
val cancelState: CancelState,
120+
) {
121+
val cancelButtonEnabled: Boolean = cancelState != CancelState.InProgress
122+
val cancelInProgressShown: Boolean = cancelState == CancelState.InProgress
123+
}
108124

109125
@LightDarkThemePreviews
110126
@Composable
@@ -117,7 +133,8 @@ private fun BookingAppointmentDetailsPreview() {
117133
staff = "Marianne Renoir",
118134
location = "238 Willow Creek Drive, Montgomery AL 36109",
119135
duration = "60 min",
120-
price = "$55.00"
136+
price = "$55.00",
137+
cancelState = CancelState.Idle,
121138
),
122139
onCancelBooking = {},
123140
modifier = Modifier.fillMaxWidth()

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ private fun BookingDetailsPreview() {
233233
staff = "Marianne Renoir",
234234
location = "238 Willow Creek Drive, Montgomery AL 36109",
235235
duration = "60 min",
236-
price = "$55.00"
236+
price = "$55.00",
237+
cancelState = CancelState.Idle,
237238
),
238239
bookingCustomerDetails = BookingCustomerDetailsModel(
239240
name = "Margarita Nikolaevna",

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsViewModel.kt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import com.woocommerce.android.viewmodel.ResourceProvider
1212
import com.woocommerce.android.viewmodel.ScopedViewModel
1313
import com.woocommerce.android.viewmodel.navArgs
1414
import dagger.hilt.android.lifecycle.HiltViewModel
15+
import kotlinx.coroutines.delay
1516
import kotlinx.coroutines.flow.MutableStateFlow
1617
import kotlinx.coroutines.flow.combine
18+
import kotlinx.coroutines.launch
19+
import java.time.Duration
1720
import javax.inject.Inject
1821

1922
@HiltViewModel
@@ -30,13 +33,15 @@ class BookingDetailsViewModel @Inject constructor(
3033

3134
// Temporary, the booking status should come from the stored object
3235
private val bookingAttendanceStatus = MutableStateFlow<BookingAttendanceStatus?>(null)
36+
private val cancelState = MutableStateFlow<CancelState>(CancelState.Idle)
3337
private val showCancelDialog = MutableStateFlow(false)
3438

3539
val state: LiveData<BookingDetailsViewState> = combine(
3640
booking,
3741
bookingAttendanceStatus,
38-
showCancelDialog
39-
) { booking, attendanceStatus, showDialog ->
42+
showCancelDialog,
43+
cancelState,
44+
) { booking, attendanceStatus, showDialog, cancel ->
4045
with(bookingMapper) {
4146
val cancelMessage = booking?.let {
4247
buildCancelDialogMessage(booking, resourceProvider)
@@ -45,7 +50,11 @@ class BookingDetailsViewModel @Inject constructor(
4550
toolbarTitle = booking?.id?.value?.let { id ->
4651
resourceProvider.getString(R.string.booking_details_title, id)
4752
} ?: "",
48-
bookingUiState = if (booking != null) buildBookingUiState(booking, attendanceStatus) else null,
53+
bookingUiState = if (booking != null) {
54+
buildBookingUiState(booking, attendanceStatus, cancel)
55+
} else {
56+
null
57+
},
4958
onCancelBooking = ::onCancelBooking,
5059
onAttendanceStatusSelected = ::onAttendanceStatusSelected,
5160
showCancelBookingDialog = showDialog,
@@ -69,14 +78,18 @@ class BookingDetailsViewModel @Inject constructor(
6978
showCancelDialog.value = false
7079
}
7180

72-
private fun onConfirmCancelBooking() {
81+
private fun onConfirmCancelBooking() = launch {
7382
// TODO Add logic to Cancel booking action
7483
showCancelDialog.value = false
84+
cancelState.value = CancelState.InProgress
85+
delay(Duration.ofSeconds(1).toMillis())
86+
cancelState.value = CancelState.Idle
7587
}
7688

7789
private suspend fun BookingMapper.buildBookingUiState(
7890
booking: Booking,
79-
attendanceStatus: BookingAttendanceStatus?
91+
attendanceStatus: BookingAttendanceStatus?,
92+
cancelState: CancelState,
8093
): BookingUiState = BookingUiState(
8194
orderId = booking.orderId,
8295
bookingSummary = booking.toBookingSummaryModel().let {
@@ -86,7 +99,7 @@ class BookingDetailsViewModel @Inject constructor(
8699
it
87100
}
88101
},
89-
bookingsAppointmentDetails = booking.toAppointmentDetailsModel(),
102+
bookingsAppointmentDetails = booking.toAppointmentDetailsModel(cancelState),
90103
bookingCustomerDetails = booking.order.customerInfo.toCustomerDetailsModel(),
91104
bookingPaymentDetails = booking.order.paymentInfo?.toPaymentDetailsModel(booking.currency)
92105
)

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/details/BookingDetailsViewState.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,8 @@ data class BookingUiState(
2727
val bookingCustomerDetails: BookingCustomerDetailsModel,
2828
val bookingPaymentDetails: BookingPaymentDetailsModel?,
2929
)
30+
31+
sealed interface CancelState {
32+
data object Idle : CancelState
33+
data object InProgress : CancelState
34+
}

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/BookingMapperTest.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.woocommerce.android.R
44
import com.woocommerce.android.model.GetLocations
55
import com.woocommerce.android.ui.bookings.compose.BookingAttendanceStatus
66
import com.woocommerce.android.ui.bookings.compose.BookingStatus
7+
import com.woocommerce.android.ui.bookings.details.CancelState
78
import com.woocommerce.android.util.CurrencyFormatter
89
import com.woocommerce.android.viewmodel.BaseUnitTest
910
import com.woocommerce.android.viewmodel.ResourceProvider
@@ -98,7 +99,7 @@ class BookingMapperTest : BaseUnitTest() {
9899
val expectedTime = "${timeFormatter.format(start)} - ${timeFormatter.format(end)}"
99100

100101
// WHEN
101-
val model = mapper.run { booking.toAppointmentDetailsModel() }
102+
val model = mapper.run { booking.toAppointmentDetailsModel(CancelState.Idle) }
102103

103104
// THEN
104105
assertThat(model.date).isEqualTo(expectedDate)
@@ -107,6 +108,7 @@ class BookingMapperTest : BaseUnitTest() {
107108
assertThat(model.location).isEqualTo("238 Willow Creek Drive, Montgomery AL 36109")
108109
assertThat(model.duration).isEqualTo("90 min")
109110
assertThat(model.price).isEqualTo("$55.00")
111+
assertThat(model.cancelState).isEqualTo(CancelState.Idle)
110112
}
111113

112114
@Test

0 commit comments

Comments
 (0)