Skip to content

Commit 2fc03f2

Browse files
authored
Merge branch 'trunk' into introduce_pos_module
2 parents c87d136 + d1720fc commit 2fc03f2

34 files changed

+630
-186
lines changed

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import kotlinx.coroutines.CoroutineScope
77
import kotlinx.coroutines.async
88
import kotlinx.coroutines.flow.Flow
99
import kotlinx.coroutines.flow.flowOf
10+
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingFilters
1011
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingUpdatePayload
11-
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
1212
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsOrderOption
1313
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsStore
1414
import org.wordpress.android.fluxc.persistence.entity.BookingEntity
@@ -24,7 +24,7 @@ class BookingsRepository @Inject constructor(
2424
page: Int,
2525
perPage: Int,
2626
query: String? = null,
27-
filters: List<BookingsFilterOption> = emptyList(),
27+
filters: BookingFilters? = null,
2828
order: BookingsOrderOption
2929
): Result<FetchResult> {
3030
val result = bookingsStore.fetchBookings(
@@ -51,7 +51,7 @@ class BookingsRepository @Inject constructor(
5151

5252
fun observeBookings(
5353
limit: Int? = null,
54-
filters: List<BookingsFilterOption> = emptyList(),
54+
filters: BookingFilters? = null,
5555
order: BookingsOrderOption
5656
): Flow<List<Booking>> =
5757
bookingsStore.observeBookings(
@@ -88,6 +88,19 @@ class BookingsRepository @Inject constructor(
8888
}
8989
}
9090

91+
suspend fun fetchResources(): Result<Unit> {
92+
val result = bookingsStore.fetchResources(site = selectedSite.get())
93+
return if (result.isError) {
94+
Result.failure(WooException(result.error))
95+
} else {
96+
Result.success(Unit)
97+
}
98+
}
99+
100+
suspend fun getResource(
101+
resourceId: Long
102+
): BookingResource? = bookingsStore.getResource(site = selectedSite.get(), resourceId = resourceId)
103+
91104
suspend fun fetchResource(
92105
resourceId: Long
93106
): Result<Unit> {
@@ -113,6 +126,9 @@ class BookingsRepository @Inject constructor(
113126
}
114127
}
115128

129+
fun observeResources(): Flow<List<BookingResource>> =
130+
bookingsStore.observeResources(site = selectedSite.get())
131+
116132
suspend fun updateAttendanceStatus(
117133
bookingId: Long,
118134
attendanceStatus: BookingEntity.AttendanceStatus,

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.woocommerce.android.ui.bookings.filter
22

3-
import androidx.annotation.StringRes
43
import com.woocommerce.android.model.UiString
54

65
/**
76
* UI model simple filter item
87
*/
98
data class BookingFilterListItem(
10-
@StringRes val title: Int,
9+
val title: UiString,
1110
val value: UiString? = null,
1211
val selected: Boolean = false,
1312
val onClick: () -> Unit = {}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ import androidx.navigation.compose.rememberNavController
3030
import com.woocommerce.android.R
3131
import com.woocommerce.android.ui.bookings.filter.attendancestatus.BookingAttendanceStatusFilterRoute
3232
import com.woocommerce.android.ui.bookings.filter.customer.BookingCustomerFilterPage
33-
import com.woocommerce.android.ui.bookings.filter.datetime.DateTimeFilterPage
33+
import com.woocommerce.android.ui.bookings.filter.datetime.DateTimeFilterRoute
34+
import com.woocommerce.android.ui.bookings.filter.teammember.BookingTeamMemberFilterRoute
3435
import com.woocommerce.android.ui.bookings.filter.type.BookingTypeFilterRoute
3536
import com.woocommerce.android.ui.compose.Render
3637
import com.woocommerce.android.ui.compose.component.Toolbar
@@ -130,7 +131,17 @@ private fun FiltersNavHost(
130131
composable(BookingFilterPage.List.route) {
131132
BookingFilterRootPage(state.items)
132133
}
134+
composable(BookingFilterPage.DateTime.route) {
135+
DateTimeFilterRoute(
136+
initialRange = state.updatedBookingFilters.dateRange
137+
) { dateRange ->
138+
state.onUpdateFilterOption(dateRange)
139+
}
140+
}
133141
composable(BookingFilterPage.TeamMember.route) {
142+
BookingTeamMemberFilterRoute(initialTeamMembers = state.updatedBookingFilters.teamMembers) { teamMember ->
143+
state.onUpdateFilterOption(teamMember)
144+
}
134145
}
135146
composable(BookingFilterPage.BookingType.route) {
136147
BookingTypeFilterRoute(initialType = state.updatedBookingFilters.bookingType) { type ->
@@ -160,9 +171,6 @@ private fun FiltersNavHost(
160171
state.onClose()
161172
}
162173
}
163-
composable(BookingFilterPage.DateTime.route) {
164-
DateTimeFilterPage()
165-
}
166174
composable(BookingFilterPage.Location.route) {
167175
}
168176
}

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ enum class BookingFilterPage {
3232
data class BookingFilterListUiState(
3333
val initialBookingFilters: BookingFilters = BookingFilters(),
3434
val updatedBookingFilters: BookingFilters = initialBookingFilters,
35+
val selectedFilterValues: SelectedFilterValues = SelectedFilterValues(),
3536
val currentPage: BookingFilterPage = BookingFilterPage.List,
3637
val dialogState: DialogState? = null,
3738
val onClose: () -> Unit = {},
@@ -40,15 +41,15 @@ data class BookingFilterListUiState(
4041
val onUpdateFilterOption: (BookingsFilterOption) -> Unit = {},
4142
val onClear: () -> Unit = {},
4243
) {
44+
data class SelectedFilterValues(val teamMemberValue: String? = null)
4345

4446
val items: List<BookingFilterListItem> = availableBookingFilters().map { page ->
4547
BookingFilterListItem(
46-
title = page.titleRes,
48+
title = UiString.UiStringRes(page.titleRes),
4749
value = page.filterValue,
4850
onClick = { openPage(page) },
4951
)
5052
}
51-
5253
val updatedBookingFiltersCount = updatedBookingFilters.enabledFiltersCount
5354

5455
val showClearButton: Boolean
@@ -62,6 +63,7 @@ data class BookingFilterListUiState(
6263

6364
val BookingFilterPage.filterValue: UiString
6465
get() = when (this) {
66+
BookingFilterPage.TeamMember -> selectedFilterValues.teamMemberValue?.let { UiString.UiStringText(it) }
6567
BookingFilterPage.BookingType -> {
6668
updatedBookingFilters.bookingType?.titleRes?.let { UiString.UiStringRes(it) }
6769
}
@@ -80,10 +82,15 @@ data class BookingFilterListUiState(
8082
UiString.UiStringText(name)
8183
}
8284

85+
BookingFilterPage.DateTime -> {
86+
updatedBookingFilters.dateRange?.let {
87+
UiString.UiStringRes(R.string.bookings_filter_date_filter_value)
88+
}
89+
}
90+
8391
BookingFilterPage.TeamMember,
8492
BookingFilterPage.ServiceEvent,
8593
BookingFilterPage.PaymentStatus,
86-
BookingFilterPage.DateTime,
8794
BookingFilterPage.Location,
8895
BookingFilterPage.List -> null
8996
} ?: UiString.UiStringRes(R.string.bookings_filter_default)
@@ -103,7 +110,7 @@ data class BookingFilterListUiState(
103110

104111
val BookingFilterPage.titleRes: Int
105112
@StringRes get() = when (this) {
106-
BookingFilterPage.TeamMember -> R.string.bookings_filter_title_team_member
113+
BookingFilterPage.TeamMember -> R.string.bookings_filter_title_member
107114
BookingFilterPage.AttendanceStatus -> R.string.bookings_filter_title_attendance_status
108115
BookingFilterPage.PaymentStatus -> R.string.bookings_filter_title_payment_status
109116
BookingFilterPage.BookingType -> R.string.bookings_filter_title_type
@@ -133,7 +140,7 @@ fun BookingFilters.updateFilterOption(bookingsFilterOption: BookingsFilterOption
133140
when (bookingsFilterOption) {
134141
is BookingsFilterOption.DateRange -> copy(dateRange = bookingsFilterOption)
135142
is BookingsFilterOption.Customer -> copy(customer = bookingsFilterOption)
136-
is BookingsFilterOption.TeamMember -> copy(teamMember = bookingsFilterOption)
143+
is BookingsFilterOption.TeamMembers -> copy(teamMembers = bookingsFilterOption)
137144
is BookingsFilterOption.AttendanceStatuses -> copy(attendanceStatuses = bookingsFilterOption)
138145
is BookingsFilterOption.PaymentStatus -> copy(paymentStatus = bookingsFilterOption)
139146
is BookingsFilterOption.BookingType -> copy(bookingType = bookingsFilterOption)

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import androidx.lifecycle.SavedStateHandle
44
import androidx.lifecycle.asLiveData
55
import com.woocommerce.android.R
66
import com.woocommerce.android.model.UiString
7+
import com.woocommerce.android.ui.bookings.BookingsRepository
78
import com.woocommerce.android.ui.bookings.filter.data.BookingFilterRepository
89
import com.woocommerce.android.ui.compose.DialogState
910
import com.woocommerce.android.viewmodel.MultiLiveEvent
1011
import com.woocommerce.android.viewmodel.ScopedViewModel
1112
import dagger.hilt.android.lifecycle.HiltViewModel
1213
import kotlinx.coroutines.flow.MutableStateFlow
14+
import kotlinx.coroutines.flow.distinctUntilChanged
1315
import kotlinx.coroutines.flow.firstOrNull
16+
import kotlinx.coroutines.flow.map
1417
import kotlinx.coroutines.flow.update
1518
import kotlinx.coroutines.launch
1619
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingFilters
@@ -21,6 +24,7 @@ import javax.inject.Inject
2124
class BookingFilterListViewModel @Inject constructor(
2225
savedStateHandle: SavedStateHandle,
2326
private val bookingFilterRepository: BookingFilterRepository,
27+
private val bookingsRepository: BookingsRepository,
2428
) : ScopedViewModel(savedStateHandle) {
2529

2630
private val _uiState = MutableStateFlow(
@@ -36,6 +40,7 @@ class BookingFilterListViewModel @Inject constructor(
3640

3741
init {
3842
getBookingFilter()
43+
observeSelectedTeamMembers()
3944
}
4045

4146
private fun onOpenPage(page: BookingFilterPage) {
@@ -66,6 +71,26 @@ class BookingFilterListViewModel @Inject constructor(
6671
}
6772
}
6873

74+
private fun observeSelectedTeamMembers() {
75+
launch {
76+
_uiState
77+
.map { state -> state.updatedBookingFilters.teamMembers.values.map { it.value } }
78+
.distinctUntilChanged()
79+
.collect { ids ->
80+
val teamMemberValue = when {
81+
ids.isEmpty() -> null
82+
ids.size == 1 -> bookingsRepository.getResource(ids.first())?.name
83+
else -> ids.size.toString()
84+
}
85+
_uiState.update { current ->
86+
current.copy(
87+
selectedFilterValues = current.selectedFilterValues.copy(teamMemberValue = teamMemberValue)
88+
)
89+
}
90+
}
91+
}
92+
}
93+
6994
private fun onClose() {
7095
if (_uiState.value.currentPage != BookingFilterPage.List) {
7196
_uiState.update { current ->

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import androidx.compose.foundation.lazy.items
1111
import androidx.compose.material3.HorizontalDivider
1212
import androidx.compose.runtime.Composable
1313
import androidx.compose.ui.Modifier
14-
import androidx.compose.ui.res.stringResource
1514
import androidx.compose.ui.unit.dp
1615
import com.woocommerce.android.ui.compose.component.WCListItemWithInlineSubtitle
1716
import com.woocommerce.android.ui.compose.component.getText
@@ -33,7 +32,7 @@ fun BookingFilterRootPage(
3332
private fun BookingFilterListRow(item: BookingFilterListItem) {
3433
Column(modifier = Modifier.fillMaxWidth()) {
3534
WCListItemWithInlineSubtitle(
36-
text = stringResource(item.title),
35+
text = item.title.getText(),
3736
subtitle = item.value?.getText().orEmpty(),
3837
modifier = Modifier
3938
.defaultMinSize(minHeight = 64.dp)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ import androidx.compose.runtime.Composable
1717
import androidx.compose.ui.Alignment
1818
import androidx.compose.ui.Modifier
1919
import androidx.compose.ui.res.painterResource
20-
import androidx.compose.ui.res.stringResource
2120
import androidx.compose.ui.text.style.TextOverflow
2221
import androidx.compose.ui.unit.dp
2322
import com.woocommerce.android.R
23+
import com.woocommerce.android.ui.compose.component.getText
2424

2525
@Composable
2626
fun BookingsFilterSelectionPage(
@@ -30,7 +30,7 @@ fun BookingsFilterSelectionPage(
3030
LazyColumn(modifier = modifier) {
3131
items(items) { item ->
3232
SelectionRow(
33-
text = stringResource(item.title),
33+
text = item.title.getText(),
3434
selected = item.selected,
3535
onClick = { item.onClick() }
3636
)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.woocommerce.android.ui.bookings.filter.attendancestatus
22

33
import androidx.annotation.StringRes
44
import com.woocommerce.android.R
5+
import com.woocommerce.android.model.UiString
56
import com.woocommerce.android.ui.bookings.filter.BookingFilterListItem
67
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption.AttendanceStatuses
78
import org.wordpress.android.fluxc.persistence.entity.BookingEntity.AttendanceStatus
@@ -12,7 +13,7 @@ data class BookingAttendanceStatusFilterUiState(
1213
) {
1314
val items: List<BookingFilterListItem> = availableAttendanceStatuses().map { status ->
1415
BookingFilterListItem(
15-
title = status.titleRes,
16+
title = UiString.UiStringRes(status.titleRes),
1617
selected = isSelected(status),
1718
onClick = { onStatusSelected(status) }
1819
)

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

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.datastore.preferences.core.Preferences
55
import androidx.datastore.preferences.core.edit
66
import androidx.datastore.preferences.core.longPreferencesKey
77
import androidx.datastore.preferences.core.stringPreferencesKey
8+
import androidx.datastore.preferences.core.stringSetPreferencesKey
89
import com.woocommerce.android.datastore.DataStoreQualifier
910
import com.woocommerce.android.datastore.DataStoreType.BOOKINGS_FILTERS
1011
import com.woocommerce.android.tools.SelectedSite
@@ -13,6 +14,7 @@ import kotlinx.coroutines.flow.Flow
1314
import kotlinx.coroutines.flow.distinctUntilChanged
1415
import kotlinx.coroutines.flow.flatMapLatest
1516
import kotlinx.coroutines.flow.map
17+
import org.wordpress.android.fluxc.model.LocalOrRemoteId
1618
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingFilters
1719
import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption
1820
import org.wordpress.android.fluxc.persistence.entity.BookingEntity
@@ -25,8 +27,9 @@ class BookingFilterRepository @Inject constructor(
2527
private val selectedSite: SelectedSite,
2628
) {
2729
// Keys are built per-site to keep selections isolated across sites
30+
private fun teamMembersKey(siteId: Int) = stringSetPreferencesKey("bfilters_${siteId}_team_members")
2831
private fun bookingTypeKey(siteId: Int) = stringPreferencesKey("bfilters_${siteId}_booking_type")
29-
private fun attendanceStatusesKey(siteId: Int) = stringPreferencesKey("bfilters_${siteId}_attendance_statuses")
32+
private fun attendanceStatusesKey(siteId: Int) = stringSetPreferencesKey("bfilters_${siteId}_attendance_statuses")
3033
private fun customerIdKey(siteId: Int) = longPreferencesKey("bfilters_${siteId}_customer_id")
3134
private fun customerNameKey(siteId: Int) = stringPreferencesKey("bfilters_${siteId}_customer_name")
3235
private fun dateBeforeKey(siteId: Int) = longPreferencesKey("bfilters_${siteId}_date_before")
@@ -37,6 +40,7 @@ class BookingFilterRepository @Inject constructor(
3740
val bookingFiltersFlow: Flow<BookingFilters> = siteIdFlow.flatMapLatest { siteId ->
3841
dataStore.data.map { prefs ->
3942
BookingFilters(
43+
teamMembers = prefs.getTeamMembers(siteId) ?: BookingsFilterOption.TeamMembers.DEFAULT,
4044
bookingType = prefs.getBookingType(siteId),
4145
attendanceStatuses = prefs.getAttendanceStatuses(siteId)
4246
?: BookingsFilterOption.AttendanceStatuses.DEFAULT,
@@ -49,6 +53,15 @@ class BookingFilterRepository @Inject constructor(
4953
suspend fun save(bookingFilters: BookingFilters) {
5054
val siteId = selectedSite.getSelectedSiteId()
5155
dataStore.edit { prefs ->
56+
// Team member
57+
val teamMembersKey = teamMembersKey(siteId)
58+
val teamMembersValues = bookingFilters.teamMembers.values
59+
if (teamMembersValues.isEmpty()) {
60+
prefs.remove(teamMembersKey)
61+
} else {
62+
prefs[teamMembersKey] = teamMembersValues.map { it.value.toString() }.toSet()
63+
}
64+
5265
// Booking type
5366
val bookingTypeKey = bookingTypeKey(siteId)
5467
val bookingTypeValue = bookingFilters.bookingType?.value?.name
@@ -65,8 +78,7 @@ class BookingFilterRepository @Inject constructor(
6578
if (attendanceStatusesValues.isEmpty()) {
6679
prefs.remove(attendanceStatusesKey)
6780
} else {
68-
prefs[attendanceStatusesKey] = attendanceStatusesValues
69-
.joinToString(ATTENDANCE_STATUSES_DELIMITER) { it.key }
81+
prefs[attendanceStatusesKey] = attendanceStatusesValues.map { it.key }.toSet()
7082
}
7183

7284
// Customer
@@ -103,6 +115,13 @@ class BookingFilterRepository @Inject constructor(
103115
}
104116
}
105117

118+
private fun Preferences.getTeamMembers(siteId: Int): BookingsFilterOption.TeamMembers? {
119+
val stored = this[teamMembersKey(siteId)] ?: return null
120+
val set = stored.mapNotNull { runCatching { LocalOrRemoteId.RemoteId(it.toLong()) }.getOrNull() }
121+
.toSet()
122+
return BookingsFilterOption.TeamMembers(set)
123+
}
124+
106125
private fun Preferences.getBookingType(siteId: Int): BookingsFilterOption.BookingType? {
107126
val stored = this[bookingTypeKey(siteId)] ?: return null
108127
val value = runCatching { BookingsFilterOption.BookingType.Type.valueOf(stored) }.getOrNull()
@@ -111,8 +130,7 @@ class BookingFilterRepository @Inject constructor(
111130

112131
private fun Preferences.getAttendanceStatuses(siteId: Int): BookingsFilterOption.AttendanceStatuses? {
113132
val stored = this[attendanceStatusesKey(siteId)] ?: return null
114-
val set = stored.split(ATTENDANCE_STATUSES_DELIMITER)
115-
.mapNotNull { runCatching { BookingEntity.AttendanceStatus.fromKey(it) }.getOrNull() }
133+
val set = stored.mapNotNull { runCatching { BookingEntity.AttendanceStatus.fromKey(it) }.getOrNull() }
116134
.filterNot { it is BookingEntity.AttendanceStatus.Unknown }
117135
.toSet()
118136
return BookingsFilterOption.AttendanceStatuses(set)
@@ -137,8 +155,4 @@ class BookingFilterRepository @Inject constructor(
137155
null
138156
}
139157
}
140-
141-
companion object {
142-
private const val ATTENDANCE_STATUSES_DELIMITER = ","
143-
}
144158
}

0 commit comments

Comments
 (0)