Skip to content

Commit

Permalink
Merge pull request #13188 from woocommerce/issue/13163-logic-to-save-…
Browse files Browse the repository at this point in the history
…hidden-sites

Issue/13163 logic to save hidden sites
  • Loading branch information
irfano authored Dec 30, 2024
2 parents 6f99cb6 + 87e92ed commit bcbbde1
Show file tree
Hide file tree
Showing 14 changed files with 219 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.woocommerce.android.datastore.DataStoreType.DASHBOARD_STATS
import com.woocommerce.android.datastore.DataStoreType.LAST_UPDATE
import com.woocommerce.android.datastore.DataStoreType.SHIPPING_LABEL_ADDRESS
import com.woocommerce.android.datastore.DataStoreType.SHIPPING_LABEL_CONFIGURATION
import com.woocommerce.android.datastore.DataStoreType.SITE_PICKER_WOO_VISIBLE_SITES
import com.woocommerce.android.datastore.DataStoreType.TOP_PERFORMER_PRODUCTS
import com.woocommerce.android.datastore.DataStoreType.TRACKER
import com.woocommerce.android.di.AppCoroutineScope
Expand Down Expand Up @@ -161,6 +162,24 @@ class DataStoreModule {
scope = CoroutineScope(appCoroutineScope.coroutineContext + Dispatchers.IO)
)

@Provides
@Singleton
@DataStoreQualifier(SITE_PICKER_WOO_VISIBLE_SITES)
fun provideWooVisibleSitesDataStore(
appContext: Context,
crashLogging: CrashLogging,
@AppCoroutineScope appCoroutineScope: CoroutineScope
) = PreferenceDataStoreFactory.create(
produceFile = {
appContext.preferencesDataStoreFile("site_picker_visible_sites")
},
corruptionHandler = ReplaceFileCorruptionHandler {
crashLogging.recordEvent("Corrupted data store. DataStore Type: ${SITE_PICKER_WOO_VISIBLE_SITES.name}")
emptyPreferences()
},
scope = CoroutineScope(appCoroutineScope.coroutineContext + Dispatchers.IO)
)

@Provides
@Singleton
@DataStoreQualifier(SHIPPING_LABEL_CONFIGURATION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ enum class DataStoreType {
TOP_PERFORMER_PRODUCTS,
COUPONS,
LAST_UPDATE,
SITE_PICKER_WOO_VISIBLE_SITES,
SHIPPING_LABEL_CONFIGURATION,
SHIPPING_LABEL_ADDRESS
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.woocommerce.android.di.AppCoroutineScope
import com.woocommerce.android.support.zendesk.ZendeskSettings
import com.woocommerce.android.tools.SelectedSite
import com.woocommerce.android.tools.SiteConnectionType
import com.woocommerce.android.ui.sitepicker.sitevisibility.VisibleWooSitesDataStore
import com.woocommerce.android.util.WooLog
import com.woocommerce.android.util.WooLog.T.LOGIN
import com.woocommerce.android.util.dispatchAndAwait
Expand All @@ -32,7 +33,8 @@ class AccountRepository @Inject constructor(
private val dispatcher: Dispatcher,
private val zendeskSettings: ZendeskSettings,
private val prefs: AppPrefs,
@AppCoroutineScope private val appCoroutineScope: CoroutineScope
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
private val siteVisibilityDataStore: VisibleWooSitesDataStore
) {
fun getUserAccount(): AccountModel? = accountStore.account.takeIf { it.userId != 0L }

Expand Down Expand Up @@ -112,8 +114,11 @@ class AccountRepository @Inject constructor(
AnalyticsTracker.clearAllData()
zendeskSettings.clearIdentity()

// Wipe user-specific preferences
prefs.resetUserPreferences()
// Wipe user-specific preferences and prefs data store
appCoroutineScope.launch {
prefs.resetUserPreferences()
siteVisibilityDataStore.clearAll()
}

selectedSite.reset()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ class SitePickerAdapter(

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = getItem(position)) {
is Header -> (holder as HeaderViewHolder).bind(item.label)
is Header -> (holder as HeaderViewHolder).bind(item.label, item.numberHiddenSites)
is WooSiteUiModel -> (holder as WooSiteViewHolder).bind(item)
is NonWooSiteUiModel -> (holder as NonWooSiteViewHolder).bind(item)
}
}

private class HeaderViewHolder(val view: MaterialTextView) : RecyclerView.ViewHolder(view) {
fun bind(@StringRes label: Int) {
view.setText(label)
fun bind(@StringRes label: Int, numHiddenSites: Int) {
view.text = view.resources.getString(label, numHiddenSites)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitePickerState
import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitePickerState.StoreListState
import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitePickerState.WooNotFoundState
import com.woocommerce.android.ui.sitepicker.sitediscovery.SitePickerSiteDiscoveryFragment
import com.woocommerce.android.ui.sitepicker.sitevisibility.WooSitesVisibilityFragment
import com.woocommerce.android.util.ChromeCustomTabUtils
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Exit
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.Logout
Expand Down Expand Up @@ -250,6 +251,9 @@ class SitePickerFragment :
handleNotice(AccountMismatchErrorFragment.JETPACK_CONNECTED_NOTICE) {
viewModel.onJetpackConnected()
}
handleResult<Boolean>(WooSitesVisibilityFragment.WOO_SITES_VISIBILITY_UPDATED) {
viewModel.onWooSitesVisibilityUpdated()
}
}

private fun updateStoreListView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitePickerEvent
import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitesListItem.Header
import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitesListItem.NonWooSiteUiModel
import com.woocommerce.android.ui.sitepicker.SitePickerViewModel.SitesListItem.WooSiteUiModel
import com.woocommerce.android.ui.sitepicker.sitevisibility.GetWooVisibleSites
import com.woocommerce.android.util.FeatureFlag
import com.woocommerce.android.util.WooLog
import com.woocommerce.android.viewmodel.LiveDataDelegate
Expand Down Expand Up @@ -64,7 +65,8 @@ class SitePickerViewModel @Inject constructor(
private val unifiedLoginTracker: UnifiedLoginTracker,
private val analyticsTrackerWrapper: AnalyticsTrackerWrapper,
private val userEligibilityFetcher: UserEligibilityFetcher,
private val experimentTracker: ExperimentTracker
private val experimentTracker: ExperimentTracker,
private val getWooVisibleSites: GetWooVisibleSites
) : ScopedViewModel(savedState) {
companion object {
private const val WOOCOMMERCE_INSTALLATION_URL = "https://wordpress.com/plugins/woocommerce/"
Expand Down Expand Up @@ -198,7 +200,7 @@ class SitePickerViewModel @Inject constructor(
)
}

private fun onSitesLoaded(sites: List<SiteModel>) {
private suspend fun onSitesLoaded(sites: List<SiteModel>) {
if (sites.isEmpty()) {
when {
loginSiteAddress != null -> showAccountMismatchScreen(loginSiteAddress!!)
Expand All @@ -222,23 +224,8 @@ class SitePickerViewModel @Inject constructor(
)
}
val selectedSiteId = selectedSiteId.value ?: wooSites.getOrNull(0)?.id
_sites.value = buildList {
if (wooSites.isNotEmpty()) {
add(Header(R.string.login_pick_store))
addAll(
wooSites.map {
WooSiteUiModel(
site = it,
isSelected = selectedSiteId == it.id
)
}
)
}
if (navArgs.openedFromLogin && nonWooSites.isNotEmpty()) {
add(Header(R.string.login_non_woo_stores_label))
addAll(nonWooSites.map { NonWooSiteUiModel(it) })
}
}
_sites.value = buildSitesList(wooSites, selectedSiteId, nonWooSites)

sitePickerViewState = sitePickerViewState.copy(
hasConnectedStores = sites.isNotEmpty(),
isPrimaryBtnVisible = wooSites.isNotEmpty(),
Expand All @@ -256,6 +243,34 @@ class SitePickerViewModel @Inject constructor(
}
}

private suspend fun buildSitesList(
wooSites: List<SiteModel>,
selectedSiteId: Int?,
nonWooSites: List<SiteModel>
): List<SitesListItem> = buildList {
if (wooSites.isNotEmpty()) {
val wooVisibleSites = getWooVisibleSites()
val numberOfHiddenSites = wooSites.size - wooVisibleSites.size
val string = when (numberOfHiddenSites) {
0 -> string.login_pick_store
else -> string.site_picker_select_store_list_header_with_hidden_sites
}
add(Header(string, numberOfHiddenSites))
addAll(
wooVisibleSites.map {
WooSiteUiModel(
site = it,
isSelected = selectedSiteId == it.id
)
}
)
}
if (navArgs.openedFromLogin && nonWooSites.isNotEmpty()) {
add(Header(string.login_non_woo_stores_label))
addAll(nonWooSites.map { NonWooSiteUiModel(it) })
}
}

/**
* Signin M1: User logged in with a URL. Here we check that login url to see
* if the site is (in this order):
Expand Down Expand Up @@ -650,6 +665,12 @@ class SitePickerViewModel @Inject constructor(
}
}

fun onWooSitesVisibilityUpdated() {
launch {
onSitesLoaded(repository.getSites())
}
}

@Parcelize
data class SitePickerViewState(
val userInfo: UserInfo? = null,
Expand Down Expand Up @@ -677,7 +698,7 @@ class SitePickerViewModel @Inject constructor(

sealed interface SitesListItem : Parcelable {
@Parcelize
data class Header(@StringRes val label: Int) : SitesListItem
data class Header(@StringRes val label: Int, val numberHiddenSites: Int = 0) : SitesListItem

@Parcelize
data class WooSiteUiModel(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.woocommerce.android.ui.sitepicker.sitevisibility

import com.woocommerce.android.ui.sitepicker.SitePickerRepository
import kotlinx.coroutines.flow.first
import org.wordpress.android.fluxc.model.SiteModel
import javax.inject.Inject

class GetWooVisibleSites @Inject constructor(
private val sitePickerRepository: SitePickerRepository,
private val visibleSitesDataStore: VisibleWooSitesDataStore
) {
suspend operator fun invoke(): List<SiteModel> =
sitePickerRepository.getSites()
.filter { it.hasWooCommerce && isSiteVisible(it.siteId) }

private suspend fun isSiteVisible(siteId: Long): Boolean {
return visibleSitesDataStore.isSiteVisible(siteId).first()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.woocommerce.android.ui.sitepicker.sitevisibility

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import com.woocommerce.android.datastore.DataStoreQualifier
import com.woocommerce.android.datastore.DataStoreType
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject

class VisibleWooSitesDataStore @Inject constructor(
@DataStoreQualifier(DataStoreType.SITE_PICKER_WOO_VISIBLE_SITES) private val dataStore: DataStore<Preferences>
) {
suspend fun updateSiteVisibilityStatus(siteIds: Map<Long, Boolean>) {
siteIds.forEach { (siteId, isVisible) ->
updateSiteVisibility(siteId, isVisible)
}
}

fun isSiteVisible(siteId: Long): Flow<Boolean> {
return dataStore.data.map { prefs -> prefs[booleanPreferencesKey(siteId.toString())] != false }
}

suspend fun clearAll() {
dataStore.edit { it.clear() }
}

private suspend fun updateSiteVisibility(siteId: Long, isVisible: Boolean) {
dataStore.edit { preferences ->
preferences[booleanPreferencesKey(siteId.toString())] = isVisible
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.extensions.navigateBackWithResult
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.composeView
import com.woocommerce.android.ui.main.AppBarStatus
import com.woocommerce.android.viewmodel.MultiLiveEvent
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ExitWithResult
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class WooSitesVisibilityFragment : BaseFragment() {
companion object {
const val WOO_SITES_VISIBILITY_UPDATED = "woo_sites_visibility_updated"
}

override val activityAppBarStatus: AppBarStatus
get() = AppBarStatus.Hidden

Expand All @@ -33,6 +39,7 @@ class WooSitesVisibilityFragment : BaseFragment() {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
is MultiLiveEvent.Event.Exit -> findNavController().navigateUp()
is ExitWithResult<*> -> navigateBackWithResult(WOO_SITES_VISIBILITY_UPDATED, event.data)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ fun WooSitesVisibilityScreen(viewModel: WooSitesVisibilityViewModel) {
state = state,
onBack = viewModel::onBackPressed,
onSaveTapped = viewModel::onSaveTapped,
onSiteSelected = viewModel::onSiteSelected,
onSiteTapped = viewModel::onSiteTapped,
modifier = Modifier.fillMaxWidth()
)
}
Expand All @@ -58,7 +58,7 @@ fun WooSitesVisibilityScreen(
state: WooStoresUiState,
onBack: () -> Unit,
onSaveTapped: () -> Unit,
onSiteSelected: (WooStoreUi) -> Unit,
onSiteTapped: (WooStoreUi) -> Unit,
modifier: Modifier = Modifier
) {
Scaffold(topBar = {
Expand Down Expand Up @@ -133,7 +133,7 @@ fun WooSitesVisibilityScreen(
)
AvailableStoresForHiding(
state = state,
onSiteSelected = onSiteSelected,
onSiteSelected = onSiteTapped,
modifier = Modifier
.padding(bottom = 16.dp)
.border(
Expand Down Expand Up @@ -237,6 +237,6 @@ fun StoreVisibilityScreenPreview() {
),
onBack = {},
onSaveTapped = {},
onSiteSelected = {}
onSiteTapped = {}
)
}
Loading

0 comments on commit bcbbde1

Please sign in to comment.