-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add site settings for third-party blocks and theme style gating #22785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
91dc79a
099c9a8
9aca388
4292ddb
3976d35
8a87215
22e13f0
1dc337b
04ab757
305ccc3
60e34fe
49a7ddc
19e614f
334c823
4bf1903
a6b3b94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package org.wordpress.android.datasets | ||
|
|
||
| import org.wordpress.android.fluxc.model.SiteModel | ||
| import org.wordpress.android.models.SiteSettingsModel | ||
|
|
||
| /** | ||
| * Provides site-level settings for a given [SiteModel]. | ||
| */ | ||
| interface SiteSettingsProvider { | ||
| fun getSettings(site: SiteModel): SiteSettingsModel? | ||
| fun isBlockEditorDefault(site: SiteModel): Boolean | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package org.wordpress.android.datasets | ||
|
|
||
| import org.wordpress.android.fluxc.model.SiteModel | ||
| import org.wordpress.android.models.SiteSettingsModel | ||
| import javax.inject.Inject | ||
| import javax.inject.Singleton | ||
|
|
||
| @Singleton | ||
| class SiteSettingsProviderImpl @Inject constructor() : | ||
| SiteSettingsProvider { | ||
| override fun getSettings(site: SiteModel): SiteSettingsModel? { | ||
| val cursor = SiteSettingsTable.getSettings(site.id.toLong()) | ||
| ?: return null | ||
| return cursor.use { | ||
| if (it.moveToFirst()) { | ||
| SiteSettingsModel().also { model -> | ||
| model.deserializeOptionsDatabaseCursor(it, null) | ||
| } | ||
| } else { | ||
| null | ||
| } | ||
| } | ||
| } | ||
|
|
||
| override fun isBlockEditorDefault(site: SiteModel): Boolean { | ||
| val editor = site.mobileEditor | ||
| if (editor.isNullOrEmpty()) return true | ||
| return site.isWPComSimpleSite || editor == GUTENBERG_EDITOR_NAME | ||
| } | ||
|
|
||
| private companion object { | ||
| const val GUTENBERG_EDITOR_NAME = "gutenberg" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| package org.wordpress.android.repositories | ||
|
|
||
| import kotlinx.coroutines.CancellationException | ||
| import kotlinx.coroutines.CoroutineDispatcher | ||
| import kotlinx.coroutines.async | ||
| import kotlinx.coroutines.awaitAll | ||
| import kotlinx.coroutines.withContext | ||
| import org.wordpress.android.fluxc.model.SiteModel | ||
| import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider | ||
| import org.wordpress.android.ui.prefs.AppPrefsWrapper | ||
| import org.wordpress.android.fluxc.persistence.EditorSettingsSqlUtils | ||
| import org.wordpress.android.modules.IO_THREAD | ||
| import org.wordpress.android.util.AppLog | ||
| import org.wordpress.android.util.AppLog.T | ||
| import rs.wordpress.api.kotlin.WpRequestResult | ||
| import javax.inject.Inject | ||
| import javax.inject.Named | ||
| import javax.inject.Singleton | ||
|
|
||
| @Singleton | ||
| class EditorSettingsRepository @Inject constructor( | ||
| private val wpApiClientProvider: WpApiClientProvider, | ||
| private val appPrefsWrapper: AppPrefsWrapper, | ||
| private val themeRepository: ThemeRepository, | ||
| @Named(IO_THREAD) private val ioDispatcher: CoroutineDispatcher | ||
| ) { | ||
| private val editorSettingsSqlUtils = EditorSettingsSqlUtils() | ||
|
|
||
| fun hasCachedCapabilities(site: SiteModel): Boolean = | ||
| appPrefsWrapper.hasSiteEditorCapabilities(site) | ||
|
|
||
| /** | ||
| * Returns whether the site is known to support the | ||
| * `wp-block-editor/v1/settings` endpoint, based on | ||
| * cached editor settings or a previously persisted | ||
| * result from [fetchEditorCapabilitiesForSite]. | ||
| */ | ||
| fun getSupportsEditorSettingsForSite(site: SiteModel): Boolean { | ||
| val hasExisting = | ||
| editorSettingsSqlUtils.getEditorSettingsForSite(site) != null | ||
| val cachedPref = | ||
| appPrefsWrapper.getSiteSupportsEditorSettings(site) | ||
| return hasExisting || cachedPref | ||
| } | ||
|
|
||
| /** | ||
| * Returns whether the site is known to support the | ||
| * `wpcom/v2/editor-assets` endpoint, based on a | ||
| * previously persisted result from | ||
| * [fetchEditorCapabilitiesForSite]. | ||
| */ | ||
| fun getSupportsEditorAssetsForSite( | ||
| site: SiteModel | ||
| ): Boolean = | ||
| appPrefsWrapper.getSiteSupportsEditorAssets(site) | ||
|
|
||
| /** | ||
| * Returns whether the site's active theme is a block | ||
| * theme, based on a previously persisted result from | ||
| * [fetchEditorCapabilitiesForSite]. | ||
| */ | ||
| fun getThemeSupportsBlockStyles( | ||
| site: SiteModel | ||
| ): Boolean = | ||
| appPrefsWrapper.getSiteThemeIsBlockTheme(site) | ||
|
|
||
| /** | ||
| * Queries the site's REST API to check whether the | ||
| * `wp-block-editor/v1/settings` and | ||
| * `wpcom/v2/editor-assets` routes are available, | ||
| * and fetches the current theme to determine if it | ||
| * is a block theme. All results are persisted so | ||
| * that [getSupportsEditorSettingsForSite], | ||
| * [getSupportsEditorAssetsForSite], and | ||
| * [getThemeSupportsBlockStyles] return them | ||
| * synchronously on future calls. | ||
| * | ||
| * @return `true` when both checks complete without | ||
| * transport-level failures. | ||
| */ | ||
| suspend fun fetchEditorCapabilitiesForSite( | ||
| site: SiteModel | ||
| ): Boolean = withContext(ioDispatcher) { | ||
| val results = awaitAll( | ||
| async { fetchRouteSupport(site) }, | ||
| async { fetchThemeBlockStyleSupport(site) } | ||
| ) | ||
| results.all { it } | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught") | ||
| private suspend fun fetchRouteSupport( | ||
| site: SiteModel | ||
| ): Boolean = try { | ||
| val client = | ||
| wpApiClientProvider.getWpApiClient(site) | ||
| val resolver = | ||
| wpApiClientProvider.getApiUrlResolver(site) | ||
| val response = | ||
| client.request { it.apiRoot().get() } | ||
|
|
||
| if (response is WpRequestResult.Success) { | ||
| val data = response.response.data | ||
| appPrefsWrapper | ||
| .setSiteSupportsEditorSettings( | ||
| site, | ||
| data.hasRouteForEndpoint( | ||
| resolver, | ||
| "/wp-block-editor/v1", | ||
| "settings" | ||
| ) | ||
| ) | ||
| appPrefsWrapper | ||
| .setSiteSupportsEditorAssets( | ||
| site, | ||
| data.hasRouteForEndpoint( | ||
| resolver, | ||
| "/wp-block-editor/v1", | ||
| "assets" | ||
| ) | ||
| ) | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
| } catch (e: CancellationException) { | ||
| throw e | ||
| } catch (e: Exception) { | ||
| AppLog.e( | ||
| T.EDITOR, | ||
| "Failed to fetch route support" + | ||
| " for site=${site.name}", | ||
| e | ||
| ) | ||
| false | ||
| } | ||
|
|
||
| @Suppress("TooGenericExceptionCaught") | ||
| private suspend fun fetchThemeBlockStyleSupport( | ||
| site: SiteModel | ||
| ): Boolean = try { | ||
| val theme = | ||
| themeRepository.fetchCurrentTheme(site) | ||
| if (theme != null) { | ||
| AppLog.d( | ||
| T.EDITOR, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is nitpicky, but why is this split into five string fragments?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it was originally four (the main one, plus a line per variable), then I added the non-variable line. 4bf1903 restores my original intent. |
||
| "EditorSettingsRepository: theme fetched for " + | ||
| "site=${site.name}, " + | ||
| "themeName=${theme.name}, " + | ||
| "isBlockTheme=${theme.isBlockTheme}" | ||
| ) | ||
| appPrefsWrapper.setSiteThemeIsBlockTheme( | ||
| site, theme.isBlockTheme | ||
| ) | ||
| true | ||
| } else { | ||
| false | ||
| } | ||
| } catch (e: CancellationException) { | ||
| throw e | ||
| } catch (e: Exception) { | ||
| AppLog.e( | ||
| T.EDITOR, | ||
| "Failed to fetch theme info" + | ||
| " for site=${site.name}", | ||
| e | ||
| ) | ||
| false | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package org.wordpress.android.repositories | ||
|
|
||
| import kotlinx.coroutines.CoroutineDispatcher | ||
| import kotlinx.coroutines.withContext | ||
| import org.wordpress.android.fluxc.model.SiteModel | ||
| import org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider | ||
| import org.wordpress.android.modules.IO_THREAD | ||
| import rs.wordpress.api.kotlin.WpRequestResult | ||
| import uniffi.wp_api.ThemeListParams | ||
| import uniffi.wp_api.ThemeStatus | ||
| import uniffi.wp_api.ThemeWithEditContext | ||
| import javax.inject.Inject | ||
| import javax.inject.Named | ||
| import javax.inject.Singleton | ||
|
|
||
| @Singleton | ||
| class ThemeRepository @Inject constructor( | ||
| private val wpApiClientProvider: WpApiClientProvider, | ||
| @Named(IO_THREAD) private val ioDispatcher: CoroutineDispatcher | ||
| ) { | ||
| /** | ||
| * Fetches the current active theme for the given site | ||
| * via the `wp/v2/themes?status=active` endpoint. | ||
| */ | ||
| suspend fun fetchCurrentTheme(site: SiteModel): ThemeWithEditContext? = | ||
| withContext(ioDispatcher) { | ||
| val client = wpApiClientProvider.getWpApiClient(site) | ||
| val response = client.request { | ||
| it.themes().listWithEditContext(ThemeListParams( | ||
| status = ThemeStatus.Active | ||
| )) | ||
| } | ||
|
|
||
| when (response) { | ||
| is WpRequestResult.Success -> | ||
| response.response.data.firstOrNull() | ||
| else -> null | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be simplified to:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice – done in 334c823