Skip to content

Commit 6d7c199

Browse files
authored
Merge pull request #14858 from woocommerce/issue/WOOMOB-1465-bookings-type-filter
[Bookings] Type filter
2 parents ee27bec + e1bee75 commit 6d7c199

File tree

11 files changed

+232
-9
lines changed

11 files changed

+232
-9
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.woocommerce.android.ui.bookings.filter
2+
3+
import androidx.annotation.StringRes
4+
5+
/**
6+
* UI model simple filter item
7+
*/
8+
data class BookingFilterListItem(
9+
@StringRes val title: Int,
10+
val value: String? = null,
11+
val onClick: () -> Unit = {}
12+
)

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import androidx.navigation.compose.NavHost
2828
import androidx.navigation.compose.composable
2929
import androidx.navigation.compose.rememberNavController
3030
import com.woocommerce.android.R
31+
import com.woocommerce.android.ui.bookings.filter.type.BookingTypeFilterRoute
3132
import com.woocommerce.android.ui.compose.component.Toolbar
3233
import com.woocommerce.android.ui.compose.component.WCColoredButton
3334
import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews
@@ -122,7 +123,9 @@ private fun FiltersNavHost(
122123
TODO()
123124
}
124125
composable(BookingFilterPage.BookingType.route) {
125-
TODO()
126+
BookingTypeFilterRoute(initialType = state.currentBookingType) { type ->
127+
state.onUpdateFilterOption(type)
128+
}
126129
}
127130
composable(BookingFilterPage.Customer.route) {
128131
TODO()

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ data class BookingFilterListUiState(
2525
val onClose: () -> Unit = {},
2626
val onShowBookings: () -> Unit = {},
2727
val openPage: (BookingFilterPage) -> Unit = {},
28+
val onUpdateFilterOption: (BookingsFilterOption) -> Unit = {}
2829
) {
2930

3031
val items: List<BookingFilterListItem> = availableBookingFilters().map { page ->
@@ -35,6 +36,11 @@ data class BookingFilterListUiState(
3536
)
3637
}
3738

39+
val currentBookingType: BookingsFilterOption.BookingType
40+
get() = newBookingFilters.getOrDefault<BookingsFilterOption.BookingType>(
41+
initialBookingFilters?.bookingType
42+
) ?: BookingsFilterOption.BookingType.Any
43+
3844
@DrawableRes
3945
val navigationIcon: Int = when (currentPage) {
4046
BookingFilterPage.List -> R.drawable.ic_gridicons_cross_24dp
@@ -60,12 +66,6 @@ data class BookingFilterListUiState(
6066
}
6167
}
6268

63-
data class BookingFilterListItem(
64-
@StringRes val title: Int,
65-
val value: String? = null,
66-
val onClick: () -> Unit = {}
67-
)
68-
6969
val BookingFilterPage.titleRes: Int
7070
@StringRes get() = when (this) {
7171
BookingFilterPage.TeamMember -> R.string.bookings_filter_title_team_member

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.firstOrNull
1111
import kotlinx.coroutines.flow.update
1212
import kotlinx.coroutines.launch
1313
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingFilters
14+
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
1415
import javax.inject.Inject
1516

1617
@HiltViewModel
@@ -24,6 +25,7 @@ class BookingFilterListViewModel @Inject constructor(
2425
onClose = ::onClose,
2526
onShowBookings = ::onShowBookings,
2627
openPage = ::onOpenPage,
28+
onUpdateFilterOption = ::onUpdateFilterOption,
2729
)
2830
)
2931
val uiState = _uiState.asLiveData()
@@ -38,6 +40,23 @@ class BookingFilterListViewModel @Inject constructor(
3840
}
3941
}
4042

43+
private fun onUpdateFilterOption(option: BookingsFilterOption) {
44+
_uiState.update { current ->
45+
val filtered = when (option) {
46+
is BookingsFilterOption.BookingType -> {
47+
current.newBookingFilters.filterNot { it is BookingsFilterOption.BookingType }
48+
}
49+
50+
else -> current.newBookingFilters.filterNot { it::class == option::class }
51+
}
52+
current.copy(
53+
newBookingFilters = filtered
54+
.plus(option)
55+
.toSet()
56+
)
57+
}
58+
}
59+
4160
private fun getBookingFilter() {
4261
launch {
4362
// We don't observe changes here, just get the current value once
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.woocommerce.android.ui.bookings.filter
2+
3+
import androidx.compose.foundation.clickable
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.defaultMinSize
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.foundation.layout.size
10+
import androidx.compose.foundation.lazy.LazyColumn
11+
import androidx.compose.foundation.lazy.items
12+
import androidx.compose.material3.HorizontalDivider
13+
import androidx.compose.material3.Icon
14+
import androidx.compose.material3.MaterialTheme
15+
import androidx.compose.material3.Text
16+
import androidx.compose.runtime.Composable
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.res.painterResource
20+
import androidx.compose.ui.res.stringResource
21+
import androidx.compose.ui.text.style.TextOverflow
22+
import androidx.compose.ui.unit.dp
23+
import com.woocommerce.android.R
24+
25+
@Composable
26+
fun SingleChoiceFilterPage(
27+
items: List<BookingFilterListItem>,
28+
selectedValue: String?,
29+
modifier: Modifier = Modifier,
30+
) {
31+
LazyColumn(modifier = modifier) {
32+
items(items) { item ->
33+
SingleChoiceRow(
34+
text = stringResource(item.title),
35+
selected = item.value == selectedValue,
36+
onClick = { item.onClick() }
37+
)
38+
}
39+
}
40+
}
41+
42+
@Composable
43+
private fun SingleChoiceRow(
44+
text: String,
45+
selected: Boolean,
46+
onClick: () -> Unit,
47+
) {
48+
Column(modifier = Modifier.fillMaxWidth()) {
49+
Row(
50+
modifier = Modifier
51+
.fillMaxWidth()
52+
.defaultMinSize(minHeight = 64.dp)
53+
.clickable(onClick = onClick)
54+
.padding(horizontal = 16.dp),
55+
verticalAlignment = Alignment.CenterVertically
56+
) {
57+
Text(
58+
text = text,
59+
style = MaterialTheme.typography.titleMedium,
60+
overflow = TextOverflow.Ellipsis,
61+
modifier = Modifier.weight(1f)
62+
)
63+
if (selected) {
64+
Icon(
65+
painter = painterResource(id = R.drawable.ic_done_secondary),
66+
contentDescription = null,
67+
modifier = Modifier.size(26.dp),
68+
tint = MaterialTheme.colorScheme.primary
69+
)
70+
}
71+
}
72+
HorizontalDivider(thickness = 0.5.dp)
73+
}
74+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.woocommerce.android.ui.bookings.filter.type
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.collectAsState
5+
import androidx.compose.runtime.getValue
6+
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
7+
import com.woocommerce.android.ui.bookings.filter.SingleChoiceFilterPage
8+
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
9+
10+
@Composable
11+
fun BookingTypeFilterRoute(
12+
initialType: BookingsFilterOption.BookingType,
13+
onTypeFilterChanged: (BookingsFilterOption.BookingType) -> Unit,
14+
) {
15+
val viewModel = hiltViewModel<BookingTypeFilterViewModel, BookingTypeFilterViewModel.Factory> { factory ->
16+
factory.create(initialType, onTypeFilterChanged)
17+
}
18+
19+
val uiState by viewModel.uiState.collectAsState()
20+
BookingTypeFilterPage(uiState)
21+
}
22+
23+
@Composable
24+
fun BookingTypeFilterPage(state: BookingTypeFilterUiState) {
25+
SingleChoiceFilterPage(
26+
items = state.items,
27+
selectedValue = state.selectedType.filterValue,
28+
)
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.woocommerce.android.ui.bookings.filter.type
2+
3+
import androidx.annotation.StringRes
4+
import com.woocommerce.android.R
5+
import com.woocommerce.android.ui.bookings.filter.BookingFilterListItem
6+
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption.BookingType
7+
8+
data class BookingTypeFilterUiState(
9+
val selectedType: BookingType = BookingType.Any,
10+
val onTypeSelected: (BookingType) -> Unit = {},
11+
) {
12+
val items: List<BookingFilterListItem> = availableBookingTypes().map { type ->
13+
BookingFilterListItem(
14+
title = type.titleRes,
15+
value = type.filterValue,
16+
onClick = { onTypeSelected(type) }
17+
)
18+
}
19+
20+
val BookingType.titleRes: Int
21+
@StringRes get() = when (this) {
22+
BookingType.Any -> R.string.bookings_filter_default
23+
BookingType.Service -> R.string.bookings_filter_type_service
24+
BookingType.Event -> R.string.bookings_filter_type_event
25+
}
26+
27+
private fun availableBookingTypes(): List<BookingType> = listOf(
28+
BookingType.Any,
29+
BookingType.Service,
30+
BookingType.Event,
31+
)
32+
}
33+
34+
val BookingType.filterValue: String?
35+
// TODO Update this with actual endpoint values
36+
get() = when (this) {
37+
BookingType.Service -> "service"
38+
BookingType.Event -> "event"
39+
BookingType.Any -> null
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.woocommerce.android.ui.bookings.filter.type
2+
3+
import androidx.lifecycle.SavedStateHandle
4+
import com.woocommerce.android.viewmodel.ScopedViewModel
5+
import dagger.assisted.Assisted
6+
import dagger.assisted.AssistedFactory
7+
import dagger.assisted.AssistedInject
8+
import dagger.hilt.android.lifecycle.HiltViewModel
9+
import kotlinx.coroutines.flow.MutableStateFlow
10+
import kotlinx.coroutines.flow.StateFlow
11+
import kotlinx.coroutines.flow.update
12+
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
13+
14+
@HiltViewModel(assistedFactory = BookingTypeFilterViewModel.Factory::class)
15+
class BookingTypeFilterViewModel @AssistedInject constructor(
16+
@Assisted private val initialType: BookingsFilterOption.BookingType,
17+
@Assisted private val onTypeFilterChanged: (BookingsFilterOption.BookingType) -> Unit,
18+
savedStateHandle: SavedStateHandle
19+
) : ScopedViewModel(savedStateHandle) {
20+
21+
private val _uiState = MutableStateFlow(
22+
BookingTypeFilterUiState(selectedType = initialType, onTypeSelected = ::onTypeSelected)
23+
)
24+
val uiState: StateFlow<BookingTypeFilterUiState> = _uiState
25+
26+
private fun onTypeSelected(type: BookingsFilterOption.BookingType) {
27+
if (_uiState.value.selectedType != type) {
28+
_uiState.update { current -> current.copy(selectedType = type) }
29+
}
30+
onTypeFilterChanged(type)
31+
}
32+
33+
@AssistedFactory
34+
interface Factory {
35+
fun create(
36+
initialType: BookingsFilterOption.BookingType,
37+
onTypeFilterChanged: (BookingsFilterOption.BookingType) -> Unit
38+
): BookingTypeFilterViewModel
39+
}
40+
}

WooCommerce/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4242,6 +4242,8 @@
42424242
<string name="bookings_filter_title_date">Date &amp; time</string>
42434243
<string name="bookings_filter_title_service_event">Service / Event</string>
42444244
<string name="bookings_filter_default">Any</string>
4245+
<string name="bookings_filter_type_service">Service</string>
4246+
<string name="bookings_filter_type_event">Event</string>
42454247

42464248
<!-- Booking details view-->
42474249
<string name="booking_details_title">Booking #%s</string>

libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsFilterOption.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ sealed interface BookingsFilterOption {
99

1010
object PaymentStatus : BookingsFilterOption
1111

12-
object BookingType : BookingsFilterOption
12+
sealed interface BookingType : BookingsFilterOption {
13+
object Any : BookingType
14+
object Service : BookingType
15+
object Event : BookingType
16+
}
1317

1418
data class Customer(val customerId: Long, val customerName: String) : BookingsFilterOption
1519

0 commit comments

Comments
 (0)