Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
29332fc
Bump GutenbergKit to v0.13.2
jkmassel Feb 6, 2026
a8cd611
Remove old EditorConfigurationBuilder and GutenbergKitWarmupHelper
jkmassel Feb 6, 2026
b913526
Simplify GutenbergKitSettingsBuilder API
jkmassel Feb 6, 2026
7780cfa
Refactor GutenbergKitActivity editor configuration
jkmassel Feb 6, 2026
06f2441
Add GutenbergEditorPreloader for background dependency fetching
jkmassel Feb 6, 2026
c9ebc73
Inject GutenbergEditorPreloader into GutenbergKitEditorFragment
jkmassel Feb 6, 2026
4b59451
Align usage with GBKit changes
jkmassel Feb 9, 2026
f94934e
Add Theme / Editor Settings repos
jkmassel Feb 9, 2026
e221a2a
Simplify preloading for debugging
jkmassel Feb 10, 2026
6b97022
fix: Pass null post ID for local drafts in GutenbergKit (#22580)
dcalhoun Feb 10, 2026
07ff670
Remove unused code from ThemeRepository, EditorDependencyStore, and G…
jkmassel Feb 10, 2026
658f1cc
Add siteSupportsEditorSettings preference to AppPrefs and AppPrefsWra…
jkmassel Feb 10, 2026
8338e67
Fix EditorSettingsRepository and add detailed logging
jkmassel Feb 10, 2026
4fa621f
Convert GutenbergKitSettingsBuilder from object to injectable class
jkmassel Feb 10, 2026
96d264f
Fetch editor settings during preloading and use preloaded dependencies
jkmassel Feb 10, 2026
82b3c49
Add editor assets support check and use SiteModel for pref APIs
jkmassel Feb 10, 2026
adbc598
Add theme block style detection and site settings UI
jkmassel Feb 10, 2026
0595a4b
Use site's "Use Theme Styles" setting for editor configuration
jkmassel Feb 10, 2026
3b19e7a
Add "Use Third-Party Blocks" site setting for editor plugin support
jkmassel Feb 10, 2026
ba0c726
Add WP.com simple site support to WpApiClient
jkmassel Feb 11, 2026
835cb6b
Build editor assets endpoint for self-hosted sites
jkmassel Feb 11, 2026
772ec00
Fix deprecation warning
jkmassel Feb 11, 2026
86516d9
Stabilize Editor Preloader
jkmassel Feb 11, 2026
67c18e0
Use firstOrNull() in ThemeRepository to avoid crash on empty response
jkmassel Feb 11, 2026
bbdee5e
Fix cursor leak and test compile errors in GutenbergKitSettingsBuilder
jkmassel Feb 11, 2026
64ce6ce
Add isLoadingMore to LaunchedEffect keys in ObserveLoadMore
jkmassel Feb 11, 2026
a673764
Add `ThemeRepositoryTest`
jkmassel Feb 11, 2026
136d3ed
Extract SiteSettingsProvider interface for testability
jkmassel Feb 11, 2026
af26f04
Extract shouldPreload method in GutenbergEditorPreloader
jkmassel Feb 11, 2026
4aa5f74
Clean up EditorDependencyStore and WpApiClientProvider
jkmassel Feb 11, 2026
1be7a1c
Remove EditorDependencyStore and inline into GutenbergEditorPreloader
jkmassel Feb 11, 2026
f7680c6
Refactor GutenbergEditorPreloader for multi-site caching
jkmassel Feb 11, 2026
fa1dc64
Pass SiteModel through GutenbergKitEditorFragment.newInstance
jkmassel Feb 11, 2026
cd7160c
Remove unused SiteSettingsModel import from test
jkmassel Feb 11, 2026
48d5a0e
Move isBlockEditorDefault into SiteSettingsProvider
jkmassel Feb 11, 2026
e55795b
Extract EditorServiceProvider to make preloader testable
jkmassel Feb 11, 2026
70468b5
Add GutenbergEditorPreloaderTest
jkmassel Feb 11, 2026
54f7c7e
Replace mock<ThemeWithEditContext> with real data class instance
jkmassel Feb 11, 2026
66f5422
Add SiteSettingsProviderImplTest
jkmassel Feb 11, 2026
450b49c
Replace mock EditorDependencies with real data class instance
jkmassel Feb 11, 2026
292aace
Update GutenbergKit to integrate/wp-android branch
jkmassel Feb 11, 2026
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 @@ -22,7 +22,7 @@
import java.io.OutputStream;

public class WordPressDB {
private static final int DATABASE_VERSION = 70;
private static final int DATABASE_VERSION = 71;


// Warning renaming DATABASE_NAME could break previous App backups (see: xml/backup_scheme.xml)
Expand Down Expand Up @@ -187,6 +187,9 @@ public WordPressDB(Context ctx) {
case 69:
// add editor theme styles site setting
mDb.execSQL(SiteSettingsModel.ADD_USE_THEME_STYLES);
case 70:
// add third-party blocks setting
mDb.execSQL(SiteSettingsModel.ADD_USE_THIRD_PARTY_BLOCKS);
}
mDb.setVersion(DATABASE_VERSION);
}
Expand Down
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,35 @@
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
val isWpComSimple = site.isWPCom && !site.isWPComAtomic
return isWpComSimple || editor == GUTENBERG_EDITOR_NAME
}

private companion object {
const val GUTENBERG_EDITOR_NAME = "gutenberg"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public class SiteSettingsModel {
private static final String JETPACK_SEARCH_SUPPORTED_COLUMN_NAME = "jetpackSearchSupported";
private static final String JETPACK_SEARCH_ENABLED_COLUMN_NAME = "jetpackSearchEnabled";
private static final String USE_THEME_STYLES_COLUMN_NAME = "useThemeStyles";
private static final String USE_THIRD_PARTY_BLOCKS_COLUMN_NAME = "useThirdPartyBlocks";

public static final String SETTINGS_TABLE_NAME = "site_settings";

Expand Down Expand Up @@ -107,6 +108,9 @@ public class SiteSettingsModel {
+ " add " + SITE_ICON_COLUMN_NAME + " INTEGER;";
public static final String ADD_USE_THEME_STYLES = "alter table " + SETTINGS_TABLE_NAME
+ " add " + USE_THEME_STYLES_COLUMN_NAME + " BOOLEAN DEFAULT 1;";
public static final String ADD_USE_THIRD_PARTY_BLOCKS = "alter table " + SETTINGS_TABLE_NAME
+ " add " + USE_THIRD_PARTY_BLOCKS_COLUMN_NAME
+ " BOOLEAN DEFAULT 0;";

public static final String CREATE_SETTINGS_TABLE_SQL =
"CREATE TABLE IF NOT EXISTS "
Expand Down Expand Up @@ -198,6 +202,7 @@ public class SiteSettingsModel {
public boolean jetpackSearchSupported;
public boolean jetpackSearchEnabled;
public boolean useThemeStyles = true;
public boolean useThirdPartyBlocks = false;
public String quotaDiskSpace;

@Override
Expand Down Expand Up @@ -243,6 +248,7 @@ && equals(timezone, otherModel.timezone)
&& jetpackSearchEnabled == otherModel.jetpackSearchEnabled
&& jetpackSearchSupported == otherModel.jetpackSearchSupported
&& useThemeStyles == otherModel.useThemeStyles
&& useThirdPartyBlocks == otherModel.useThirdPartyBlocks
&& maxLinks == otherModel.maxLinks
&& equals(defaultPostFormat, otherModel.defaultPostFormat)
&& holdForModeration != null
Expand Down Expand Up @@ -309,6 +315,7 @@ public void copyFrom(SiteSettingsModel other) {
jetpackSearchSupported = other.jetpackSearchSupported;
jetpackSearchEnabled = other.jetpackSearchEnabled;
useThemeStyles = other.useThemeStyles;
useThirdPartyBlocks = other.useThirdPartyBlocks;
if (other.holdForModeration != null) {
holdForModeration = new ArrayList<>(other.holdForModeration);
}
Expand Down Expand Up @@ -374,6 +381,7 @@ public void deserializeOptionsDatabaseCursor(Cursor cursor, SparseArrayCompat<Ca
jetpackSearchSupported = getBooleanFromCursor(cursor, JETPACK_SEARCH_SUPPORTED_COLUMN_NAME);
jetpackSearchEnabled = getBooleanFromCursor(cursor, JETPACK_SEARCH_ENABLED_COLUMN_NAME);
useThemeStyles = getBooleanFromCursor(cursor, USE_THEME_STYLES_COLUMN_NAME);
useThirdPartyBlocks = getBooleanFromCursor(cursor, USE_THIRD_PARTY_BLOCKS_COLUMN_NAME);

String moderationKeys = getStringFromCursor(cursor, MODERATION_KEYS_COLUMN_NAME);
String denylistKeys = getStringFromCursor(cursor, DENYLIST_KEYS_COLUMN_NAME);
Expand Down Expand Up @@ -467,6 +475,7 @@ public ContentValues serializeToDatabase() {
values.put(JETPACK_SEARCH_SUPPORTED_COLUMN_NAME, jetpackSearchSupported);
values.put(JETPACK_SEARCH_ENABLED_COLUMN_NAME, jetpackSearchEnabled);
values.put(USE_THEME_STYLES_COLUMN_NAME, useThemeStyles);
values.put(USE_THIRD_PARTY_BLOCKS_COLUMN_NAME, useThirdPartyBlocks);

StringBuilder moderationKeys = new StringBuilder();
if (holdForModeration != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,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 @@ -259,6 +260,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 @@ -4,6 +4,10 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import org.wordpress.android.datasets.SiteSettingsProvider
import org.wordpress.android.datasets.SiteSettingsProviderImpl
import org.wordpress.android.ui.posts.EditorServiceProvider
import org.wordpress.android.ui.posts.EditorServiceProviderImpl
import org.wordpress.android.ui.posts.IPostFreshnessChecker
import org.wordpress.android.ui.posts.PostFreshnessCheckerImpl
import javax.inject.Singleton
Expand All @@ -13,5 +17,18 @@ import javax.inject.Singleton
class PostModule {
@Singleton
@Provides
fun providePostFreshnessChecker(): IPostFreshnessChecker = PostFreshnessCheckerImpl()
fun providePostFreshnessChecker(): IPostFreshnessChecker =
PostFreshnessCheckerImpl()

@Singleton
@Provides
fun provideSiteSettingsProvider(
impl: SiteSettingsProviderImpl
): SiteSettingsProvider = impl

@Singleton
@Provides
fun provideEditorServiceProvider(
impl: EditorServiceProviderImpl
): EditorServiceProvider = impl
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package org.wordpress.android.repositories

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
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()

/**
* 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 hasExistingEditorSettings =
editorSettingsSqlUtils.getEditorSettingsForSite(site) != null
val cachedPref =
appPrefsWrapper.getSiteSupportsEditorSettings(site)
val supports = hasExistingEditorSettings || cachedPref
AppLog.d(
T.EDITOR,
"EditorSettingsRepository: getSupportsEditorSettings" +
" site=${site.name}" +
" hasExistingEditorSettings=$hasExistingEditorSettings" +
" cachedPref=$cachedPref" +
" result=$supports"
)
return supports
}

/**
* 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 {
val supports =
appPrefsWrapper.getSiteSupportsEditorAssets(site)
AppLog.d(
T.EDITOR,
"EditorSettingsRepository: getSupportsEditorAssets" +
" site=${site.name}" +
" result=$supports"
)
return supports
}

/**
* Returns whether the site's active theme is a block
* theme, based on a previously persisted result from
* [fetchEditorCapabilitiesForSite].
*/
fun getThemeSupportsBlockStyles(site: SiteModel): Boolean {
val supports =
appPrefsWrapper.getSiteThemeIsBlockTheme(site)
AppLog.d(
T.EDITOR,
"EditorSettingsRepository: getThemeSupportsBlockStyles" +
" site=${site.name}" +
" result=$supports"
)
return supports
}

/**
* Queries the site's REST API root index 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] can return them
* synchronously on future calls.
*/
suspend fun fetchEditorCapabilitiesForSite(
site: SiteModel
) = withContext(ioDispatcher) {
AppLog.d(
T.EDITOR,
"EditorSettingsRepository: fetching editor" +
" capabilities for site=${site.name}"
)

// supervisorScope so that a failure in one fetch
// doesn't cancel the other
supervisorScope {
launch { fetchRouteSupport(site) }
launch { fetchThemeBlockStyleSupport(site) }
}
}

private suspend fun fetchRouteSupport(site: SiteModel) {
val client = wpApiClientProvider.getWpApiClient(site)
val response = client.request { it.apiRoot().get() }

when (response) {
is WpRequestResult.Success -> {
val data = response.response.data
val supportsSettings = data
.hasRoute("/wp-block-editor/v1/settings")
val supportsAssets = data
.hasRoute("/wpcom/v2/editor-assets")

AppLog.d(
T.EDITOR,
"EditorSettingsRepository: API root fetched" +
" for site=${site.name}" +
" supportsEditorSettings=$supportsSettings" +
" supportsEditorAssets=$supportsAssets"
)

appPrefsWrapper.setSiteSupportsEditorSettings(
site, supportsSettings
)
appPrefsWrapper.setSiteSupportsEditorAssets(
site, supportsAssets
)
}
else -> {
AppLog.w(
T.EDITOR,
"EditorSettingsRepository: API root request" +
" failed for site=${site.name}" +
" response=$response"
)

appPrefsWrapper.setSiteSupportsEditorSettings(
site, false
)
appPrefsWrapper.setSiteSupportsEditorAssets(
site, false
)
}
}
}

private suspend fun fetchThemeBlockStyleSupport(
site: SiteModel
) {
val theme = themeRepository.fetchCurrentTheme(site)
val isBlockTheme = theme?.isBlockTheme ?: false

AppLog.d(
T.EDITOR,
"EditorSettingsRepository: theme fetched" +
" for site=${site.name}" +
" themeName=${theme?.name}" +
" isBlockTheme=$isBlockTheme"
)

appPrefsWrapper.setSiteThemeIsBlockTheme(site, isBlockTheme)
}
}
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
}
}
}
Loading