Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
import org.wordpress.android.ui.posts.AddCategoryFragment;
import org.wordpress.android.ui.posts.EditPostActivity;
import org.wordpress.android.ui.posts.GutenbergKitActivity;
import org.wordpress.android.ui.posts.editor.GutenbergKitEditorFragment;
import org.wordpress.android.ui.posts.EditPostPublishSettingsFragment;
import org.wordpress.android.ui.posts.EditPostSettingsFragment;
import org.wordpress.android.ui.posts.HistoryListFragment;
Expand Down Expand Up @@ -255,6 +256,8 @@ public interface AppComponent {

void inject(GutenbergKitActivity object);

void inject(GutenbergKitEditorFragment object);

void inject(EditPostSettingsFragment object);

void inject(PostSettingsListDialogFragment object);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import org.wordpress.android.ui.mysite.items.DashboardItemsViewModelSlice
import org.wordpress.android.ui.pages.SnackbarMessageHolder
import org.wordpress.android.ui.mediapicker.MediaPickerActivity
import org.wordpress.android.ui.posts.BasicDialogViewModel
import org.wordpress.android.repositories.EditorSettingsRepository
import org.wordpress.android.ui.posts.GutenbergEditorPreloader
import org.wordpress.android.ui.sitecreation.misc.SiteCreationSource
import org.wordpress.android.util.BuildConfigWrapper
import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper
Expand All @@ -43,9 +45,7 @@ import javax.inject.Inject
import javax.inject.Named
import org.wordpress.android.ui.mysite.cards.applicationpassword.ApplicationPasswordViewModelSlice
import org.wordpress.android.ui.mysite.items.listitem.SiteCapabilityChecker
import org.wordpress.android.ui.posts.GutenbergKitWarmupHelper
import org.wordpress.android.ui.utils.UiString
import org.wordpress.android.repositories.EditorSettingsRepository

@Suppress("LargeClass", "LongMethod", "LongParameterList")
class MySiteViewModel @Inject constructor(
Expand All @@ -65,8 +65,8 @@ class MySiteViewModel @Inject constructor(
private val dashboardCardsViewModelSlice: DashboardCardsViewModelSlice,
private val dashboardItemsViewModelSlice: DashboardItemsViewModelSlice,
private val applicationPasswordViewModelSlice: ApplicationPasswordViewModelSlice,
private val gutenbergKitWarmupHelper: GutenbergKitWarmupHelper,
private val siteCapabilityChecker: SiteCapabilityChecker,
private val gutenbergEditorPreloader: GutenbergEditorPreloader,
private val editorSettingsRepository: EditorSettingsRepository,
) : ScopedViewModel(mainDispatcher) {
private val _onSnackbarMessage = MutableLiveData<Event<SnackbarMessageHolder>>()
Expand Down Expand Up @@ -169,7 +169,7 @@ class MySiteViewModel @Inject constructor(
if (isPullToRefresh) {
siteCapabilityChecker.clearCacheForSite(site.siteId)
}
buildDashboardOrSiteItems(site)
buildDashboardOrSiteItems(site, forceRefresh = isPullToRefresh)
launch {
fetchEditorCapabilitiesWithSnackbar(
site,
Expand Down Expand Up @@ -211,8 +211,7 @@ class MySiteViewModel @Inject constructor(
Event(
SnackbarMessageHolder(
UiString.UiStringRes(
R.string
.site_settings_fetch_failed
R.string.site_settings_fetch_failed
)
)
)
Expand Down Expand Up @@ -284,7 +283,7 @@ class MySiteViewModel @Inject constructor(
dashboardCardsViewModelSlice.onCleared()
dashboardItemsViewModelSlice.onCleared()
accountDataViewModelSlice.onCleared()
gutenbergKitWarmupHelper.clearWarmupState()
gutenbergEditorPreloader.clear()
super.onCleared()
}

Expand Down Expand Up @@ -317,7 +316,10 @@ class MySiteViewModel @Inject constructor(
}
}

private fun buildDashboardOrSiteItems(site: SiteModel) {
private fun buildDashboardOrSiteItems(
site: SiteModel,
forceRefresh: Boolean = false
) {
siteInfoHeaderCardViewModelSlice.buildCard(site)
applicationPasswordViewModelSlice.buildCard(site)
if (shouldShowDashboard(site)) {
Expand All @@ -327,8 +329,11 @@ class MySiteViewModel @Inject constructor(
dashboardItemsViewModelSlice.buildItems(site)
dashboardCardsViewModelSlice.clearValue()
}
// Trigger GutenbergView warmup for the selected site
gutenbergKitWarmupHelper.warmupIfNeeded(site, viewModelScope)
if (forceRefresh) {
gutenbergEditorPreloader.refreshPreloading(site, viewModelScope)
} else {
gutenbergEditorPreloader.preloadIfNeeded(site, viewModelScope)
}
}

private fun onSitePicked(site: SiteModel) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.wordpress.android.ui.posts

import org.wordpress.android.datasets.SiteSettingsProvider
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.repositories.EditorSettingsRepository
import org.wordpress.android.util.config.GutenbergKitPluginsFeature
import javax.inject.Inject
import javax.inject.Singleton

/**
* Single source of truth for whether a given editor capability
* applies to a site. Combines:
*
* 1. A global feature-flag gate (the top-level GutenbergKit flag,
* plus any capability-specific remote flag such as
* [GutenbergKitPluginsFeature]).
* 2. A site-level capability cache populated by
* [EditorSettingsRepository.fetchEditorCapabilitiesForSite].
* 3. The user's per-site toggle stored in
* [SiteSettingsProvider].
*
* Both the settings UI ([SiteSettingsFragment]) and the editor
* configuration builder consult this resolver so they cannot
* drift out of agreement.
*/
@Singleton
class EditorCapabilityResolver @Inject constructor(
private val gutenbergKitFeatureChecker: GutenbergKitFeatureChecker,
private val gutenbergKitPluginsFeature: GutenbergKitPluginsFeature,
private val editorSettingsRepository: EditorSettingsRepository,
private val siteSettingsProvider: SiteSettingsProvider,
) {
fun resolveThirdPartyBlocks(site: SiteModel): Resolved {
if (!gutenbergKitFeatureChecker.isGutenbergKitEnabled()) {
return Resolved.Hidden
}
if (!gutenbergKitPluginsFeature.isEnabled()) {
return Resolved.Hidden
}
if (!editorSettingsRepository.getSupportsEditorAssetsForSite(site)) {
return Resolved.Unsupported(Resolved.UnsupportedReason.CapabilityMissing)
}
val userEnabled = siteSettingsProvider
.getSettings(site)
?.useThirdPartyBlocks
?: DEFAULT_USE_THIRD_PARTY_BLOCKS
return Resolved.Available(userEnabled)
}

fun resolveThemeStyles(site: SiteModel): Resolved {
if (!gutenbergKitFeatureChecker.isGutenbergKitEnabled()) {
return Resolved.Hidden
}
if (!editorSettingsRepository.getSupportsEditorSettingsForSite(site)) {
return Resolved.Unsupported(Resolved.UnsupportedReason.CapabilityMissing)
}
val userEnabled = siteSettingsProvider
.getSettings(site)
?.useThemeStyles
?: DEFAULT_USE_THEME_STYLES
val advisory = if (!editorSettingsRepository.getThemeSupportsBlockStyles(site)) {
Resolved.AdvisoryReason.ThemeNotBlockTheme
} else {
null
}
return Resolved.Available(userEnabled, advisory)
}

companion object {
private const val DEFAULT_USE_THIRD_PARTY_BLOCKS = false
private const val DEFAULT_USE_THEME_STYLES = true
}
}

/**
* Resolved state for an editor capability for a specific site.
*
* [shouldApplyInEditor] collapses the state to a single boolean
* for editor-config callers; the UI layer branches on the full
* sealed hierarchy to pick the correct visibility / disabled /
* advisory-note treatment.
*/
sealed class Resolved {
/**
* Globally disabled (feature flag off). The setting row is
* hidden; the editor does not apply the capability.
*/
data object Hidden : Resolved()

/**
* Globally enabled, but this site cannot use the capability.
* The setting row is shown but disabled, with a reason the
* UI can surface to the user.
*/
data class Unsupported(val reason: UnsupportedReason) : Resolved()

/**
* Globally enabled and toggle-able for this site.
* [userEnabled] is the current user preference.
* [advisory] optionally attaches an informational note — the
* toggle is still honoured regardless.
*/
data class Available(
val userEnabled: Boolean,
val advisory: AdvisoryReason? = null,
) : Resolved()

enum class UnsupportedReason { CapabilityMissing }
enum class AdvisoryReason { ThemeNotBlockTheme }

val shouldApplyInEditor: Boolean
get() = this is Available && userEnabled
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.wordpress.android.ui.posts

import android.content.Context
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import org.wordpress.gutenberg.model.EditorConfiguration
import org.wordpress.gutenberg.model.EditorDependencies

/**
* Abstracts the creation and preparation of the GutenbergKit
* [EditorService] so callers can be tested without the real
* service.
*/
interface EditorServiceProvider {
suspend fun prepare(
context: Context,
configuration: EditorConfiguration,
coroutineScope: CoroutineScope
): EditorDependencies
}

@InstallIn(SingletonComponent::class)
@Module
interface EditorServiceProviderModule {
@Binds
fun bindEditorServiceProvider(impl: EditorServiceProviderImpl): EditorServiceProvider
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.wordpress.android.ui.posts

import android.content.Context
import kotlinx.coroutines.CoroutineScope
import org.wordpress.gutenberg.model.EditorConfiguration
import org.wordpress.gutenberg.model.EditorDependencies
import org.wordpress.gutenberg.services.EditorService
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class EditorServiceProviderImpl @Inject constructor() :
EditorServiceProvider {
override suspend fun prepare(
context: Context,
configuration: EditorConfiguration,
coroutineScope: CoroutineScope
): EditorDependencies {
val service = EditorService.create(
context = context,
configuration = configuration,
coroutineScope = coroutineScope
)
return service.prepare(null)
}
}
Loading
Loading