Skip to content

Commit 938529e

Browse files
authored
CMM-801 create sync taxonomies calls in fluxC (#22273)
* Storing taxonomies * detekt * Show local data in DataView * Fixing duplicated calls * detekt * Fixing test
1 parent 29dbe55 commit 938529e

File tree

6 files changed

+160
-23
lines changed

6 files changed

+160
-23
lines changed

WordPress/src/main/java/org/wordpress/android/ui/accounts/applicationpassword/ApplicationPasswordsViewModel.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ class ApplicationPasswordsViewModel @Inject constructor(
5050
accountStore = accountStore,
5151
ioDispatcher = ioDispatcher
5252
) {
53+
init {
54+
initialize()
55+
}
56+
5357
override fun getSupportedSorts(): List<DataViewDropdownItem> = listOf(
5458
DataViewDropdownItem(id = SORT_BY_NAME_ID, titleRes = R.string.application_password_name_sort),
5559
DataViewDropdownItem(id = SORT_BY_CREATED_ID, titleRes = R.string.application_password_created_sort),

WordPress/src/main/java/org/wordpress/android/ui/dataview/DataViewViewModel.kt

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,12 @@ open class DataViewViewModel @Inject constructor(
9191

9292
init {
9393
appLogWrapper.d(AppLog.T.MAIN, "$logTag init")
94-
this.initialize()
9594
}
9695

9796
protected open fun initialize() {
9897
launch {
9998
restorePrefs()
100-
fetchData()
99+
fetchData(localData = getLocalData())
101100

102101
debouncedQuery
103102
.debounce(SEARCH_DELAY_MS)
@@ -145,19 +144,32 @@ open class DataViewViewModel @Inject constructor(
145144
}
146145
}
147146

148-
private fun fetchData(isRefreshing: Boolean = false) {
149-
if (networkUtilsWrapper.isNetworkAvailable()) {
150-
val currentState = _uiState.value
151-
val isLoadingMore = currentState.currentPage > INITIAL_PAGE
152-
153-
updateState { state ->
154-
state.copy(
155-
loadingState = if (isLoadingMore) LoadingState.LOADING_MORE else LoadingState.LOADING,
156-
isRefreshing = isRefreshing
157-
)
147+
private fun fetchData(isRefreshing: Boolean = false, localData: List<DataViewItem> = emptyList()) {
148+
launch {
149+
if (localData.isNotEmpty()) {
150+
updateState { currentState ->
151+
currentState.copy(
152+
items = localData,
153+
loadingState = LoadingState.LOADED,
154+
)
155+
}
158156
}
159157

160-
launch {
158+
if (networkUtilsWrapper.isNetworkAvailable()) {
159+
val currentState = _uiState.value
160+
val isLoadingMore = currentState.currentPage > INITIAL_PAGE
161+
162+
updateState { state ->
163+
state.copy(
164+
loadingState = if (isLoadingMore || localData.isNotEmpty()) {
165+
LoadingState.LOADING_MORE
166+
} else {
167+
LoadingState.LOADING
168+
},
169+
isRefreshing = isRefreshing
170+
)
171+
}
172+
161173
val items = performNetworkRequest(
162174
page = currentState.currentPage,
163175
searchQuery = currentState.searchQuery,
@@ -173,17 +185,20 @@ open class DataViewViewModel @Inject constructor(
173185

174186
updateItems(items, isLoadingMore)
175187
updateState { it.copy(isRefreshing = false) }
176-
}
177-
} else {
178-
updateState {
179-
it.copy(
180-
loadingState = LoadingState.OFFLINE,
181-
isRefreshing = false
182-
)
188+
} else if (localData.isEmpty()) {
189+
// Only show error if local data is empty
190+
updateState {
191+
it.copy(
192+
loadingState = LoadingState.OFFLINE,
193+
isRefreshing = false
194+
)
195+
}
183196
}
184197
}
185198
}
186199

200+
open fun getLocalData(): List<DataViewItem> = emptyList()
201+
187202
// resetPaging() is now handled by the helper function above
188203

189204
fun onRefreshData() {

WordPress/src/main/java/org/wordpress/android/ui/subscribers/SubscribersViewModel.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ class SubscribersViewModel @Inject constructor(
7575
private val _uiEvent = MutableStateFlow<UiEvent?>(null)
7676
val uiEvent = _uiEvent
7777

78+
init {
79+
initialize()
80+
}
81+
7882
override fun getSupportedFilters(): List<DataViewDropdownItem> {
7983
return listOf(
8084
DataViewDropdownItem(

WordPress/src/main/java/org/wordpress/android/ui/taxonomies/TermsViewModel.kt

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ import kotlinx.coroutines.flow.asStateFlow
1414
import kotlinx.coroutines.launch
1515
import kotlinx.coroutines.withContext
1616
import org.wordpress.android.R
17+
import org.wordpress.android.fluxc.Dispatcher
18+
import org.wordpress.android.fluxc.generated.TaxonomyActionBuilder
1719
import org.wordpress.android.fluxc.model.SiteModel
20+
import org.wordpress.android.fluxc.model.TermModel
21+
import org.wordpress.android.fluxc.model.TermsModel
1822
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
1923
import org.wordpress.android.fluxc.store.AccountStore
24+
import org.wordpress.android.fluxc.store.TaxonomyStore
2025
import org.wordpress.android.fluxc.store.TaxonomyStore.DEFAULT_TAXONOMY_CATEGORY
2126
import org.wordpress.android.fluxc.store.TaxonomyStore.DEFAULT_TAXONOMY_TAG
27+
import org.wordpress.android.fluxc.store.TaxonomyStore.FetchTermsResponsePayload
2228
import org.wordpress.android.fluxc.utils.AppLogWrapper
2329
import org.wordpress.android.modules.IO_THREAD
2430
import org.wordpress.android.modules.UI_THREAD
@@ -31,10 +37,10 @@ import org.wordpress.android.ui.mysite.SelectedSiteRepository
3137
import org.wordpress.android.util.AppLog
3238
import org.wordpress.android.util.NetworkUtilsWrapper
3339
import rs.wordpress.api.kotlin.WpRequestResult
34-
import uniffi.wp_api.TermEndpointType
35-
import uniffi.wp_api.TermListParams
3640
import uniffi.wp_api.AnyTermWithEditContext
3741
import uniffi.wp_api.TermCreateParams
42+
import uniffi.wp_api.TermEndpointType
43+
import uniffi.wp_api.TermListParams
3844
import uniffi.wp_api.TermUpdateParams
3945
import uniffi.wp_api.WpApiParamOrder
4046
import uniffi.wp_api.WpApiParamTermsOrderBy
@@ -74,6 +80,8 @@ class TermsViewModel @Inject constructor(
7480
private val wpApiClientProvider: WpApiClientProvider,
7581
private val appLogWrapper: AppLogWrapper,
7682
private val selectedSiteRepository: SelectedSiteRepository,
83+
private val taxonomyStore: TaxonomyStore,
84+
private val fluxCDispatcher: Dispatcher, // Used to include FluxC in the flow (local terms store)
7785
accountStore: AccountStore,
7886
@Named(UI_THREAD) mainDispatcher: CoroutineDispatcher,
7987
sharedPrefs: SharedPreferences,
@@ -112,6 +120,7 @@ class TermsViewModel @Inject constructor(
112120
fun initialize(taxonomySlug: String, isHierarchical: Boolean) {
113121
this.taxonomySlug = taxonomySlug
114122
this.isHierarchical = isHierarchical
123+
taxonomyStore.onRegister()
115124
initialize()
116125
}
117126

@@ -197,6 +206,20 @@ class TermsViewModel @Inject constructor(
197206
_termDetailState.value = null
198207
}
199208

209+
override fun getLocalData(): List<DataViewItem> = when(val site = selectedSiteRepository.getSelectedSite()) {
210+
null -> emptyList()
211+
else -> {
212+
val terms = taxonomyStore.getTermsForSite(site, this.taxonomySlug)
213+
terms.map { term ->
214+
convertToDataViewItem(
215+
terms,
216+
term,
217+
isHierarchical
218+
)
219+
}
220+
}
221+
}
222+
200223
override fun getSupportedSorts(): List<DataViewDropdownItem> = if (isHierarchical) {
201224
listOf()
202225
} else {
@@ -232,6 +255,11 @@ class TermsViewModel @Inject constructor(
232255
allTerms
233256
}
234257

258+
// Store terms when they are not filtered
259+
if (sortedTerms.isNotEmpty() && filter == null) {
260+
storeTerms(selectedSite, sortedTerms)
261+
}
262+
235263
// Convert to DataViewItems and return
236264
sortedTerms.map { term ->
237265
// Do not use hierarchical indentation when the user is searching terms
@@ -267,6 +295,30 @@ class TermsViewModel @Inject constructor(
267295
return result
268296
}
269297

298+
private suspend fun storeTerms(site: SiteModel, terms: List<AnyTermWithEditContext>) = withContext(ioDispatcher) {
299+
val termsResponsePayload = FetchTermsResponsePayload(
300+
TermsModel(
301+
terms.map { term ->
302+
TermModel(
303+
term.id.toInt(),
304+
site.id,
305+
term.id,
306+
taxonomySlug,
307+
term.name,
308+
term.slug,
309+
term.description,
310+
term.parent ?: 0,
311+
term.parent != null,
312+
term.count.toInt()
313+
)
314+
},
315+
),
316+
site,
317+
taxonomySlug
318+
)
319+
fluxCDispatcher.dispatch(TaxonomyActionBuilder.newFetchedTermsAction(termsResponsePayload))
320+
}
321+
270322
fun saveTerm() {
271323
viewModelScope.launch {
272324
val selectedSite = selectedSiteRepository.getSelectedSite()
@@ -395,6 +447,32 @@ class TermsViewModel @Inject constructor(
395447
)
396448
}
397449

450+
private fun convertToDataViewItem(
451+
allTerms: List<TermModel>,
452+
term: TermModel,
453+
useHierarchicalIndentation: Boolean
454+
): DataViewItem {
455+
val indentation = if (useHierarchicalIndentation) {
456+
getHierarchicalIndentation(allTerms, term)
457+
} else {
458+
0
459+
}
460+
return DataViewItem(
461+
id = term.remoteTermId,
462+
image = null,
463+
title = term.name,
464+
fields = listOf(
465+
DataViewItemField(
466+
value = context.resources.getString(R.string.term_count, term.postCount),
467+
valueType = DataViewFieldType.TEXT,
468+
)
469+
),
470+
skipEndPositioning = true,
471+
data = term,
472+
indentation = (indentation * INDENTATION_IN_DP).dp
473+
)
474+
}
475+
398476

399477
/**
400478
* Returns an integer representation of the hierarchical indentation for the given term.
@@ -419,6 +497,29 @@ class TermsViewModel @Inject constructor(
419497
return indentation
420498
}
421499

500+
/**
501+
* Returns an integer representation of the hierarchical indentation for the given term.
502+
*/
503+
private fun getHierarchicalIndentation(
504+
allTerms: List<TermModel>,
505+
term: TermModel?
506+
): Int {
507+
if (term == null) return 0
508+
509+
val termsById = allTerms.associateBy { it.remoteTermId }
510+
var indentation = 0
511+
var currentParentId = term.parentRemoteId
512+
513+
while (currentParentId > 0) {
514+
val parent = termsById[currentParentId]
515+
if (parent == null) break
516+
indentation++
517+
currentParentId = parent.parentRemoteId
518+
}
519+
520+
return indentation
521+
}
522+
422523
private suspend fun getTermsList(
423524
site: SiteModel,
424525
page: Int,

WordPress/src/test/java/org/wordpress/android/ui/dataview/DataViewViewModelTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ class DataViewViewModelTest : BaseUnitTest() {
503503
accountStore,
504504
ioDispatcher
505505
) {
506+
init {
507+
initialize()
508+
}
506509
/**
507510
* Flag to control when the full initialization happens. How It Works
508511
* 1. During construction: shouldInitialize is false, so initialize() does nothing

WordPress/src/test/java/org/wordpress/android/ui/taxonomies/TermsViewModelTest.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import org.mockito.kotlin.verify
1515
import org.mockito.kotlin.whenever
1616
import org.wordpress.android.BaseUnitTest
1717
import org.wordpress.android.R
18+
import org.wordpress.android.fluxc.Dispatcher
1819
import org.wordpress.android.fluxc.model.SiteModel
1920
import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider
2021
import org.wordpress.android.fluxc.store.AccountStore
22+
import org.wordpress.android.fluxc.store.TaxonomyStore
2123
import org.wordpress.android.fluxc.store.TaxonomyStore.DEFAULT_TAXONOMY_CATEGORY
2224
import org.wordpress.android.fluxc.store.TaxonomyStore.DEFAULT_TAXONOMY_TAG
2325
import org.wordpress.android.fluxc.utils.AppLogWrapper
@@ -47,6 +49,12 @@ class TermsViewModelTest : BaseUnitTest() {
4749
@Mock
4850
private lateinit var networkUtilsWrapper: NetworkUtilsWrapper
4951

52+
@Mock
53+
private lateinit var taxonomyStore: TaxonomyStore
54+
55+
@Mock
56+
private lateinit var fluxCDispatcher: Dispatcher
57+
5058
@Before
5159
fun setUp() {
5260
MockitoAnnotations.openMocks(this)
@@ -62,7 +70,9 @@ class TermsViewModelTest : BaseUnitTest() {
6270
mainDispatcher = testDispatcher(),
6371
sharedPrefs = sharedPrefs,
6472
networkUtilsWrapper = networkUtilsWrapper,
65-
ioDispatcher = testDispatcher()
73+
ioDispatcher = testDispatcher(),
74+
taxonomyStore = taxonomyStore,
75+
fluxCDispatcher = fluxCDispatcher
6676
)
6777
}
6878

0 commit comments

Comments
 (0)