diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingsRepository.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingsRepository.kt index 255f5ba657b..5506c8f0f8f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingsRepository.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/BookingsRepository.kt @@ -4,6 +4,7 @@ import com.woocommerce.android.WooException import com.woocommerce.android.tools.SelectedSite import kotlinx.coroutines.flow.Flow import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption +import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsOrderOption import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsStore import org.wordpress.android.fluxc.persistence.entity.BookingEntity import javax.inject.Inject @@ -16,14 +17,16 @@ class BookingsRepository @Inject constructor( page: Int, perPage: Int, query: String? = null, - filters: List = emptyList() + filters: List = emptyList(), + order: BookingsOrderOption ): Result { val result = bookingsStore.fetchBookings( site = selectedSite.get(), perPage = perPage, page = page, query = query, - filters = filters + filters = filters, + order = order ) return if (result.isError) { Result.failure(WooException(result.error)) @@ -41,12 +44,14 @@ class BookingsRepository @Inject constructor( fun observeBookings( limit: Int? = null, - filters: List = emptyList() + filters: List = emptyList(), + order: BookingsOrderOption ): Flow> = bookingsStore.observeBookings( site = selectedSite.get(), limit = limit, - filters = filters + filters = filters, + order = order ) data class FetchResult( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandler.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandler.kt index 170d4f31f7f..cd1a2499822 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandler.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandler.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption +import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsOrderOption import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject @@ -29,13 +30,23 @@ class BookingListHandler @Inject constructor( private val searchQuery = MutableStateFlow(null) private val filters = MutableStateFlow>(emptyList()) + private val sortBy = MutableStateFlow(BookingListSortOption.NewestToOldest) private val searchResults = MutableStateFlow(emptyList()) @OptIn(ExperimentalCoroutinesApi::class) - val bookingsFlow: Flow> = combine(searchQuery, filters, page) { query, filters, page -> + val bookingsFlow: Flow> = combine( + searchQuery, + filters, + page, + sortBy + ) { query, filters, page, sortBy -> if (query.isNullOrEmpty()) { - bookingsRepository.observeBookings(limit = page * PAGE_SIZE, filters) + bookingsRepository.observeBookings( + limit = page * PAGE_SIZE, + filters = filters, + order = sortBy.toBookingsOrderOption() + ) } else { searchResults } @@ -43,7 +54,8 @@ class BookingListHandler @Inject constructor( suspend fun loadBookings( searchQuery: String? = null, - filters: List = emptyList() + filters: List = emptyList(), + sortBy: BookingListSortOption ): Result = mutex.withLock { // Reset pagination attributes page.value = 1 @@ -51,6 +63,7 @@ class BookingListHandler @Inject constructor( this.searchQuery.value = searchQuery this.filters.value = filters + this.sortBy.value = sortBy return@withLock if (searchQuery == null) { fetchBookings() @@ -73,11 +86,13 @@ class BookingListHandler @Inject constructor( private suspend fun fetchBookings(): Result { val isSearching = !searchQuery.value.isNullOrEmpty() + val order = sortBy.value.toBookingsOrderOption() return bookingsRepository.fetchBookings( page = page.value, perPage = PAGE_SIZE, query = searchQuery.value, - filters = filters.value + filters = filters.value, + order = order ).onSuccess { result -> canLoadMore.set(result.hasMorePages) if (result.hasMorePages) { @@ -88,4 +103,9 @@ class BookingListHandler @Inject constructor( } }.map { } } + + private fun BookingListSortOption.toBookingsOrderOption() = when (this) { + BookingListSortOption.NewestToOldest -> BookingsOrderOption.DESC + BookingListSortOption.OldestToNewest -> BookingsOrderOption.ASC + } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListScreen.kt index 08d61d30c37..18eef87c087 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListScreen.kt @@ -49,6 +49,7 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.bookings.compose.BookingAttendanceStatus import com.woocommerce.android.ui.bookings.compose.BookingStatus import com.woocommerce.android.ui.bookings.compose.BookingSummary +import com.woocommerce.android.ui.bookings.compose.BookingSummaryModel import com.woocommerce.android.ui.compose.component.InfiniteListHandler import com.woocommerce.android.ui.compose.component.Toolbar import com.woocommerce.android.ui.compose.component.WCPrimaryTabRow @@ -300,7 +301,7 @@ private fun BookingListPreview() { bookings = List(20) { BookingListItem( id = it.toLong(), - summary = com.woocommerce.android.ui.bookings.compose.BookingSummaryModel( + summary = BookingSummaryModel( date = "Aug 20, 2024", name = "Women’s Haircut", customerName = "Margarita Nikolaevna", diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModel.kt index 984a5488a1b..65c2c1a9d7e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModel.kt @@ -38,13 +38,7 @@ class BookingListViewModel @Inject constructor( key = "searchQuery" ) - private val sortOptionsByTab = MutableStateFlow( - mapOf( - BookingListTab.Today to BookingListSortOption.NewestToOldest, - BookingListTab.Upcoming to BookingListSortOption.NewestToOldest, - BookingListTab.All to BookingListSortOption.NewestToOldest, - ) - ) + private val sortOption = savedStateHandle.getStateFlow(viewModelScope, BookingListSortOption.NewestToOldest) private val isSortSheetVisible = MutableStateFlow(false) @@ -75,11 +69,10 @@ class BookingListViewModel @Inject constructor( val state = combine( contentState, selectedTab, - sortOptionsByTab, + sortOption, isSortSheetVisible, searchState - ) { contentState, selectedTab, sortOptionsByTab, sheetVisible, searchState -> - val sortOption = sortOptionsByTab[selectedTab] ?: BookingListSortOption.NewestToOldest + ) { contentState, selectedTab, sortOption, sheetVisible, searchState -> BookingListViewState( contentState = contentState, tabState = BookingListTabState( @@ -119,15 +112,21 @@ class BookingListViewModel @Inject constructor( .debounce { if (it.isNullOrEmpty()) 0L else AppConstants.SEARCH_TYPING_DELAY_MS } - - merge(selectedTab, queryFlow) - .collectLatest { - // Cancel any ongoing fetch or load more operations - bookingsFetchJob?.cancel() - bookingsLoadMoreJob?.cancel() - - bookingsFetchJob = fetchBookings(BookingListLoadingState.Loading) - } + val sortFlow = sortOption.drop(1) // Skip the initial value to avoid double fetch on init + + merge(selectedTab, queryFlow, sortFlow).collectLatest { + // Cancel any ongoing fetch or load more operations + bookingsFetchJob?.cancel() + bookingsLoadMoreJob?.cancel() + + bookingsFetchJob = fetchBookings( + initialLoadingState = if (it is BookingListSortOption) { + BookingListLoadingState.Refreshing + } else { + BookingListLoadingState.Loading + } + ) + } } } @@ -135,7 +134,8 @@ class BookingListViewModel @Inject constructor( loadingState.value = initialLoadingState bookingListHandler.loadBookings( searchQuery = searchQuery.value, - filters = prepareFilters() + filters = prepareFilters(), + sortBy = sortOption.value ).onFailure { triggerEvent(MultiLiveEvent.Event.ShowSnackbar(R.string.bookings_fetch_error)) } @@ -170,11 +170,8 @@ class BookingListViewModel @Inject constructor( } private fun onSortOptionSelected(option: BookingListSortOption) { - val tab = selectedTab.value - sortOptionsByTab.value = sortOptionsByTab.value.toMutableMap() - .also { it[tab] = option } + sortOption.value = option isSortSheetVisible.value = false - // TODO Apply the selected sorting to the data for the active tab } private fun onSortDismiss() { diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandlerTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandlerTest.kt index 78139b80abe..5bc23fb55eb 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandlerTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListHandlerTest.kt @@ -33,11 +33,19 @@ class BookingListHandlerTest : BaseUnitTest() { private val availablePages = 3 private val bookingsRepository: BookingsRepository = mock { val results = MutableStateFlow(emptyList()) - on { observeBookings(any(), any()) } doAnswer { invocation -> + on { observeBookings(any(), any(), any()) } doAnswer { invocation -> val limit = invocation.getArgument(0) results.map { it.take(limit) } } - onBlocking { fetchBookings(any(), any(), anyOrNull(), any()) } doAnswer InlineClassesAnswer { invocation -> + onBlocking { + fetchBookings( + any(), + any(), + anyOrNull(), + any(), + any() + ) + } doAnswer InlineClassesAnswer { invocation -> val page = invocation.getArgument(0) val perPage = invocation.getArgument(1) val canLoadMore = page < availablePages @@ -57,7 +65,7 @@ class BookingListHandlerTest : BaseUnitTest() { @Test fun `given repository returns bookings, when observing bookings flow, then returns bookings`() = testBlocking { val sampleBookings = List(10) { getSampleBooking(it) } - given(bookingsRepository.observeBookings(any(), any())).willReturn(flowOf(sampleBookings)) + given(bookingsRepository.observeBookings(any(), any(), any())).willReturn(flowOf(sampleBookings)) val bookings = bookingListHandler.bookingsFlow.first() @@ -67,7 +75,10 @@ class BookingListHandlerTest : BaseUnitTest() { @Test fun `given no search query and force refresh, when loading bookings, then fetches from repository`() = testBlocking { - val result = bookingListHandler.loadBookings(searchQuery = null) + val result = bookingListHandler.loadBookings( + searchQuery = null, + sortBy = BookingListSortOption.NewestToOldest + ) val bookings = bookingListHandler.bookingsFlow.first() assertThat(result.isSuccess).isTrue() @@ -78,10 +89,21 @@ class BookingListHandlerTest : BaseUnitTest() { fun `given repository fetch fails, when loading bookings with force refresh, then returns failure`() = testBlocking { val exception = Exception("Network error") - given(bookingsRepository.fetchBookings(page = any(), perPage = any(), query = anyOrNull(), filters = any())) + given( + bookingsRepository.fetchBookings( + page = any(), + perPage = any(), + query = anyOrNull(), + filters = any(), + order = any() + ) + ) .willReturn(Result.failure(exception)) - val result = bookingListHandler.loadBookings(searchQuery = null) + val result = bookingListHandler.loadBookings( + searchQuery = null, + sortBy = BookingListSortOption.NewestToOldest + ) assertThat(result.isFailure).isTrue() assertThat(result.exceptionOrNull()).isEqualTo(exception) @@ -97,13 +119,14 @@ class BookingListHandlerTest : BaseUnitTest() { page = any(), perPage = any(), query = anyOrNull(), - filters = any() + filters = any(), + order = any() ) } @Test fun `when load more is called and can load more is true, then fetches next page`() = testBlocking { - bookingListHandler.loadBookings() + bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) val result = bookingListHandler.loadMore() val bookings = bookingListHandler.bookingsFlow.first() @@ -114,7 +137,7 @@ class BookingListHandlerTest : BaseUnitTest() { @Test fun `when last page is reached, then can load more becomes false`() = testBlocking { - bookingListHandler.loadBookings() + bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) var result: Result? = null repeat(availablePages - 1) { @@ -126,18 +149,19 @@ class BookingListHandlerTest : BaseUnitTest() { page = intThat { it > availablePages }, perPage = any(), query = anyOrNull(), - filters = any() + filters = any(), + order = any() ) } @Test fun `when load bookings is called, then pagination resets`() = testBlocking { // First load and load more to advance page - bookingListHandler.loadBookings() + bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) bookingListHandler.loadMore() // Load bookings again - should reset to page 1 - bookingListHandler.loadBookings() + bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) val bookings = bookingListHandler.bookingsFlow.first() assertThat(bookings).hasSize(BookingListHandler.PAGE_SIZE) @@ -145,7 +169,7 @@ class BookingListHandlerTest : BaseUnitTest() { @Test fun `when bookings flow is observed with pagination, then limit increases correctly`() = testBlocking { - bookingListHandler.loadBookings() + bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) val initialBookings = bookingListHandler.bookingsFlow.first() assertThat(initialBookings).hasSize(BookingListHandler.PAGE_SIZE) @@ -154,15 +178,19 @@ class BookingListHandlerTest : BaseUnitTest() { val moreBookings = bookingListHandler.bookingsFlow.first() @Suppress("UnusedFlow") - verify(bookingsRepository).observeBookings(limit = eq(2 * BookingListHandler.PAGE_SIZE), filters = any()) + verify(bookingsRepository).observeBookings( + limit = eq(2 * BookingListHandler.PAGE_SIZE), + filters = any(), + order = any() + ) assertThat(moreBookings).hasSize(2 * BookingListHandler.PAGE_SIZE) } @Test fun `when concurrent load operations occur, then operations are synchronized`() = testBlocking { // Launch multiple concurrent load operations - val job1 = launch { bookingListHandler.loadBookings() } - val job2 = launch { bookingListHandler.loadBookings() } + val job1 = launch { bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) } + val job2 = launch { bookingListHandler.loadBookings(sortBy = BookingListSortOption.NewestToOldest) } val job3 = launch { bookingListHandler.loadMore() } job1.join() diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModelTest.kt index e378cd018a3..9f5195be0af 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/bookings/list/BookingListViewModelTest.kt @@ -34,7 +34,8 @@ class BookingListViewModelTest : BaseUnitTest() { onBlocking { loadBookings( searchQuery = anyOrNull(), - filters = any() + filters = any(), + sortBy = any() ) } doReturn Result.success(Unit) onBlocking { loadMore() } doReturn Result.success(Unit) @@ -67,7 +68,7 @@ class BookingListViewModelTest : BaseUnitTest() { advanceUntilIdle() // THEN - verify(bookingListHandler).loadBookings(searchQuery = eq(null), filters = any()) + verify(bookingListHandler).loadBookings(searchQuery = eq(null), filters = any(), sortBy = any()) val state = viewModel.state.getOrAwaitValue().contentState assertThat(state.bookings).hasSize(1) @@ -106,7 +107,8 @@ class BookingListViewModelTest : BaseUnitTest() { // THEN verify(bookingListHandler, times(2)).loadBookings( searchQuery = eq(null), - filters = any() + filters = any(), + sortBy = any() ) } @@ -146,7 +148,7 @@ class BookingListViewModelTest : BaseUnitTest() { fun `when booking handler fails to load, then show error snackbar`() = testBlocking { // GIVEN setup { - whenever(bookingListHandler.loadBookings(searchQuery = anyOrNull(), filters = any())) + whenever(bookingListHandler.loadBookings(searchQuery = anyOrNull(), filters = any(), sortBy = any())) .thenReturn(Result.failure(Exception("Network error"))) } @@ -213,7 +215,8 @@ class BookingListViewModelTest : BaseUnitTest() { BookingListTab.Upcoming.asDateRangeFilter() } ) - ) + ), + sortBy = any() ) } @@ -231,43 +234,6 @@ class BookingListViewModelTest : BaseUnitTest() { assertThat(withSheet.sortBottomSheetState).isNotNull() } - @Test - fun `when selecting sort in one tab, then other tabs keep their own selection`() = testBlocking { - // GIVEN - setup() - - // Open sheet on Today and select OldestToNewest - val s1 = viewModel.state.getOrAwaitValue() - s1.controlsState.onSortClick() - val sheet1 = viewModel.state.getOrAwaitValue().sortBottomSheetState!! - sheet1.onSelect(BookingListSortOption.OldestToNewest) - - // Sheet should be hidden after selection - val afterSelectToday = viewModel.state.getOrAwaitValue() - assertThat(afterSelectToday.sortBottomSheetState).isNull() - - // Re-open and ensure Today remembers OldestToNewest - afterSelectToday.controlsState.onSortClick() - val sheetTodayAgain = viewModel.state.getOrAwaitValue().sortBottomSheetState!! - assertThat(sheetTodayAgain.selectedOption).isEqualTo(BookingListSortOption.OldestToNewest) - sheetTodayAgain.onDismiss() - - // Switch to Upcoming and verify default is NewestToOldest - val stateAfterDismiss = viewModel.state.getOrAwaitValue() - stateAfterDismiss.tabState.onTabChanged(BookingListTab.Upcoming) - val upcomingState = viewModel.state.getOrAwaitValue() - upcomingState.controlsState.onSortClick() - val sheetUpcoming = viewModel.state.getOrAwaitValue().sortBottomSheetState!! - assertThat(sheetUpcoming.selectedOption).isEqualTo(BookingListSortOption.NewestToOldest) - - // Switch back to Today and ensure it still holds its own selection (OldestToNewest) - upcomingState.tabState.onTabChanged(BookingListTab.Today) - val backToToday = viewModel.state.getOrAwaitValue() - backToToday.controlsState.onSortClick() - val sheetBackToToday = viewModel.state.getOrAwaitValue().sortBottomSheetState!! - assertThat(sheetBackToToday.selectedOption).isEqualTo(BookingListSortOption.OldestToNewest) - } - @Test fun `when search query is changed, then state is updated`() = testBlocking { // GIVEN @@ -296,7 +262,8 @@ class BookingListViewModelTest : BaseUnitTest() { // THEN verify(bookingListHandler).loadBookings( searchQuery = eq("test query"), - filters = any() + filters = any(), + sortBy = any() ) } @@ -318,7 +285,8 @@ class BookingListViewModelTest : BaseUnitTest() { assertThat(clearedState.searchState.isSearchActive).isFalse() verify(bookingListHandler, times(2)).loadBookings( searchQuery = eq(null), - filters = any() + filters = any(), + sortBy = any() ) } diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsOrderOption.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsOrderOption.kt new file mode 100644 index 00000000000..575bc88f3a6 --- /dev/null +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsOrderOption.kt @@ -0,0 +1,13 @@ +package org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings + +/** + * Represents the sort order for bookings. + * + * Use [value] when building query parameters. + */ +enum class BookingsOrderOption(val value: String) { + ASC("asc"), + DESC("desc"); + + override fun toString(): String = value +} diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsRestClient.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsRestClient.kt index 5e92c94ff2d..6f53c9d0baf 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsRestClient.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsRestClient.kt @@ -19,12 +19,14 @@ class BookingsRestClient @Inject constructor( const val DEFAULT_PER_PAGE = 25 // Number of items to fetch in a single request } + @Suppress("LongParameterList") suspend fun fetchBookings( site: SiteModel, perPage: Int, page: Int, query: String?, - filters: List + filters: List, + order: BookingsOrderOption ): WooPayload> { val endpoint = WOOCOMMERCE.bookings.pathV2Bookings @@ -33,6 +35,8 @@ class BookingsRestClient @Inject constructor( path = endpoint, clazz = Array::class.java, params = mapOf( + "orderby" to "start_date", + "order" to order.value, "per_page" to perPage.toString(), "page" to page.toString(), "search" to query @@ -52,6 +56,7 @@ class BookingsRestClient @Inject constructor( filter.before?.let { set("start_date_before", it.toString()) } filter.after?.let { set("start_date_after", it.toString()) } } + is BookingsFilterOption.Customer -> set("customer", filter.customerId.toString()) } } diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsStore.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsStore.kt index 7d96673571c..7f046757e07 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsStore.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/network/rest/wpcom/wc/bookings/BookingsStore.kt @@ -29,10 +29,11 @@ class BookingsStore @Inject constructor( perPage: Int = BookingsRestClient.DEFAULT_PER_PAGE, page: Int = 1, query: String? = null, - filters: List = emptyList() + filters: List = emptyList(), + order: BookingsOrderOption ): WooResult { return coroutineEngine.withDefaultContext(AppLog.T.API, this, "fetchBookings") { - val response = bookingsRestClient.fetchBookings(site, perPage, page, query, filters) + val response = bookingsRestClient.fetchBookings(site, perPage, page, query, filters, order) when { response.isError -> WooResult(response.error) response.result != null -> { @@ -62,8 +63,9 @@ class BookingsStore @Inject constructor( fun observeBookings( site: SiteModel, limit: Int? = null, - filters: List = emptyList() - ): Flow> = bookingsDao.observeBookings(site.localId(), limit, filters) + filters: List = emptyList(), + order: BookingsOrderOption + ): Flow> = bookingsDao.observeBookings(site.localId(), limit, filters, order) private fun BookingDto.toEntity(localSiteId: LocalId): BookingEntity = BookingEntity( id = RemoteId(id), diff --git a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/BookingsDao.kt b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/BookingsDao.kt index 4bb46eda4af..0f4ae8393a2 100644 --- a/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/BookingsDao.kt +++ b/libs/fluxc-plugin/src/main/kotlin/org/wordpress/android/fluxc/persistence/dao/BookingsDao.kt @@ -7,6 +7,7 @@ import androidx.room.Query import kotlinx.coroutines.flow.Flow import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsFilterOption +import org.wordpress.android.fluxc.network.rest.wpcom.wc.bookings.BookingsOrderOption import org.wordpress.android.fluxc.persistence.entity.BookingEntity @Dao @@ -18,11 +19,14 @@ interface BookingsDao { AND (:startDateBefore IS NULL OR start < :startDateBefore) AND (:startDateAfter IS NULL OR start > :startDateAfter) AND (:customerId IS NULL OR customerId = :customerId) - ORDER BY dateCreated DESC + ORDER BY + CASE WHEN :order = 'ASC' THEN start END ASC, + CASE WHEN :order = 'DESC' THEN start END DESC LIMIT CASE WHEN :limit IS NULL THEN -1 ELSE :limit END """ } + @Suppress("LongParameterList") @Query(DEFAULT_SELECT_QUERY) fun observeBookings( localSiteId: LocalId, @@ -30,8 +34,10 @@ interface BookingsDao { startDateBefore: Long?, startDateAfter: Long?, customerId: Long?, + order: BookingsOrderOption ): Flow> + @Suppress("LongParameterList") @Query(DEFAULT_SELECT_QUERY) suspend fun getBookings( localSiteId: LocalId, @@ -39,6 +45,7 @@ interface BookingsDao { startDateBefore: Long?, startDateAfter: Long?, customerId: Long?, + order: BookingsOrderOption ): List @Insert(onConflict = OnConflictStrategy.REPLACE) @@ -53,7 +60,8 @@ interface BookingsDao { fun observeBookings( localSiteId: LocalId, limit: Int? = null, - filters: List = emptyList() + filters: List = emptyList(), + order: BookingsOrderOption ): Flow> { val dateRangeFilter = filters.filterIsInstance().firstOrNull() val customerFilter = filters.filterIsInstance().firstOrNull() @@ -64,13 +72,15 @@ interface BookingsDao { startDateBefore = dateRangeFilter?.before?.epochSecond, startDateAfter = dateRangeFilter?.after?.epochSecond, customerId = customerFilter?.customerId, + order = order ) } suspend fun getBookings( localSiteId: LocalId, limit: Int? = null, - filters: List = emptyList() + filters: List = emptyList(), + order: BookingsOrderOption ): List { val dateRangeFilter = filters.filterIsInstance().firstOrNull() val customerFilter = filters.filterIsInstance().firstOrNull() @@ -81,6 +91,7 @@ interface BookingsDao { startDateBefore = dateRangeFilter?.before?.epochSecond, startDateAfter = dateRangeFilter?.after?.epochSecond, customerId = customerFilter?.customerId, + order = order ) } }