From 550a588d6bea621b9388f76034a2821d6ed8b466 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Mon, 7 Apr 2025 11:33:17 -0400 Subject: [PATCH 01/22] refactor: Provide editor settings to GutenbergKit --- .../gutenberg/GutenbergKitEditorFragment.java | 25 +++++ .../fluxc/action/EditorSettingsAction.kt | 13 +++ .../action/EditorSettingsActionBuilder.kt | 10 ++ .../android/fluxc/model/EditorSettings.kt | 62 +++++++++++++ .../fluxc/store/EditorSettingsStore.kt | 92 +++++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsActionBuilder.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index 8f6ea997a8d7..c67d2867a3a9 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -25,6 +25,9 @@ import com.android.volley.toolbox.ImageLoader; import com.google.gson.Gson; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.editor.BuildConfig; import org.wordpress.android.editor.EditorEditMediaListener; import org.wordpress.android.editor.EditorFragmentAbstract; @@ -575,4 +578,26 @@ public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { public void onConnectionStatusChange(boolean isConnected) { // Unused, no-op retained for the shared interface with Gutenberg } + + @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) + public void onEditorSettingsChanged(EditorSettingsStore.OnEditorSettingsChanged event) { + if (mGutenbergView == null || event.editorSettings == null) + return; + + EditorConfiguration config = mGutenbergView.getEditorConfiguration(); + config.editorSettings = event.editorSettings.toBundle(); + mGutenbergView.setEditorConfiguration(config); + } + + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + super.onStop(); + EventBus.getDefault().unregister(this); + } } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt new file mode 100644 index 000000000000..66f18e460485 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt @@ -0,0 +1,13 @@ +package org.wordpress.android.fluxc.action + +import org.wordpress.android.fluxc.annotations.Action +import org.wordpress.android.fluxc.annotations.ActionEnum +import org.wordpress.android.fluxc.annotations.action.IAction +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.store.EditorSettingsStore.FetchEditorSettingsPayload + +@ActionEnum +enum class EditorSettingsAction : IAction { + @Action(payloadType = FetchEditorSettingsPayload::class) + FETCH_EDITOR_SETTINGS +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsActionBuilder.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsActionBuilder.kt new file mode 100644 index 000000000000..c3b4b309e0a5 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsActionBuilder.kt @@ -0,0 +1,10 @@ +package org.wordpress.android.fluxc.action + +import org.wordpress.android.fluxc.annotations.action.Action +import org.wordpress.android.fluxc.store.EditorSettingsStore.FetchEditorSettingsPayload + +object EditorSettingsActionBuilder { + fun newFetchEditorSettingsAction(payload: FetchEditorSettingsPayload): Action { + return Action(EditorSettingsAction.FETCH_EDITOR_SETTINGS, payload) + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt new file mode 100644 index 000000000000..26e034f6c98e --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -0,0 +1,62 @@ +package org.wordpress.android.fluxc.model + +import android.os.Bundle +import com.google.gson.JsonObject +import com.google.gson.annotations.SerializedName + +data class EditorSettings( + @SerializedName("colors") val colors: List? = null, + @SerializedName("gradients") val gradients: List? = null, + @SerializedName("styles") val styles: String? = null, + @SerializedName("features") val features: String? = null, + @SerializedName("isBlockBasedTheme") val isBlockBasedTheme: Boolean = false, + @SerializedName("galleryWithImageBlocks") val galleryWithImageBlocks: Boolean = false, + @SerializedName("quoteBlockV2") val quoteBlockV2: Boolean = false, + @SerializedName("listBlockV2") val listBlockV2: Boolean = false, + @SerializedName("hasBlockTemplates") val hasBlockTemplates: Boolean = false, + val rawSettings: JsonObject? = null +) { + fun toBundle(): Bundle { + val bundle = Bundle() + + colors?.map { it.toBundle() }?.let { + bundle.putParcelableArrayList("colors", ArrayList(it)) + } + + gradients?.map { it.toBundle() }?.let { + bundle.putParcelableArrayList("gradients", ArrayList(it)) + } + + styles?.let { bundle.putString("styles", it) } + features?.let { bundle.putString("features", it) } + bundle.putBoolean("isBlockBasedTheme", isBlockBasedTheme) + bundle.putBoolean("galleryWithImageBlocks", galleryWithImageBlocks) + bundle.putBoolean("quoteBlockV2", quoteBlockV2) + bundle.putBoolean("listBlockV2", listBlockV2) + bundle.putBoolean("hasBlockTemplates", hasBlockTemplates) + + rawSettings?.let { json -> + json.entrySet().forEach { entry -> + when (val value = entry.value) { + is com.google.gson.JsonPrimitive -> { + if (value.isBoolean) { + bundle.putBoolean(entry.key, value.asBoolean) + } else if (value.isNumber) { + bundle.putDouble(entry.key, value.asDouble) + } else if (value.isString) { + bundle.putString(entry.key, value.asString) + } + } + is com.google.gson.JsonArray -> { + // Handle arrays if needed + } + is com.google.gson.JsonObject -> { + // Handle nested objects if needed + } + } + } + } + + return bundle + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt new file mode 100644 index 000000000000..df22276be45e --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt @@ -0,0 +1,92 @@ +package org.wordpress.android.fluxc.store + +import com.google.gson.Gson +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import org.wordpress.android.fluxc.Dispatcher +import org.wordpress.android.fluxc.Payload +import org.wordpress.android.fluxc.action.EditorSettingsAction +import org.wordpress.android.fluxc.action.EditorSettingsAction.FETCH_EDITOR_SETTINGS +import org.wordpress.android.fluxc.annotations.action.Action +import org.wordpress.android.fluxc.model.EditorSettings +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError +import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Error +import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Success +import org.wordpress.android.fluxc.tools.CoroutineEngine +import org.wordpress.android.util.AppLog +import javax.inject.Inject +import javax.inject.Singleton + +private const val EDITOR_SETTINGS_REQUEST_PATH = "wp-block-editor/v1/settings?context=mobile" + +@Singleton +class EditorSettingsStore @Inject constructor( + private val reactNativeStore: ReactNativeStore, + private val coroutineEngine: CoroutineEngine, + dispatcher: Dispatcher +) : Store(dispatcher) { + class FetchEditorSettingsPayload(val site: SiteModel) : Payload() + + data class OnEditorSettingsChanged( + val editorSettings: EditorSettings?, + val siteId: Int, + val causeOfChange: EditorSettingsAction + ) : Store.OnChanged() { + constructor(error: EditorSettingsError, causeOfChange: EditorSettingsAction) : + this(editorSettings = null, siteId = -1, causeOfChange = causeOfChange) { + this.error = error + } + } + + class EditorSettingsError(var message: String? = null) : OnChangedError + + @Subscribe(threadMode = ThreadMode.ASYNC) + override fun onAction(action: Action<*>) { + val actionType = action.type as? EditorSettingsAction ?: return + when (actionType) { + FETCH_EDITOR_SETTINGS -> { + coroutineEngine.launch( + AppLog.T.API, + this, + EditorSettingsStore::class.java.simpleName + ": On FETCH_EDITOR_SETTINGS" + ) { + val payload = action.payload as FetchEditorSettingsPayload + fetchEditorSettings(payload.site, actionType) + } + } + } + } + + override fun onRegister() { + AppLog.d(AppLog.T.API, EditorSettingsStore::class.java.simpleName + " onRegister") + } + + private suspend fun fetchEditorSettings(site: SiteModel, action: EditorSettingsAction) { + val response = reactNativeStore.executeGetRequest(site, EDITOR_SETTINGS_REQUEST_PATH, false) + + when (response) { + is Success -> { + if (response.result == null || !response.result.isJsonObject) { + emitChange(OnEditorSettingsChanged( + EditorSettingsError("Response does not contain editor settings"), + action + )) + return + } + + val editorSettings = Gson().fromJson(response.result, EditorSettings::class.java) + editorSettings.rawSettings = response.result.asJsonObject + val onChanged = OnEditorSettingsChanged(editorSettings, site.id, action) + emitChange(onChanged) + } + is Error -> { + val onChanged = OnEditorSettingsChanged( + EditorSettingsError(response.error.message), + action + ) + emitChange(onChanged) + } + } + } +} From 2727408562855352e6040d78e8d9a63f11764f7a Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Mon, 7 Apr 2025 12:42:00 -0400 Subject: [PATCH 02/22] refactor: Move onEditorSettingsChanged subscription to EditPostActivity Mirror theme change subscription. --- .../android/ui/posts/EditPostActivity.kt | 8 +++++ .../gutenberg/GutenbergKitEditorFragment.java | 35 ++++++------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index 050a347fe988..b041839f9155 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -4065,6 +4065,14 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor postEditorAnalyticsSession?.editorSettingsFetched(editorThemeSupport.isBlockBasedTheme, event.endpoint.value) } + @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) + fun onEditorSettingsChanged(event: EditorSettingsStore.OnEditorSettingsChanged) { + if (site.id != event.siteId) return + val editorSettings = event.editorSettings ?: return + + (editorFragment as? GutenbergKitEditorFragment)?.updateEditorSettings(editorSettings.toBundle()) + } + // EditPostActivityHook methods override fun getEditPostRepository() = editPostRepository override fun getSite() = siteModel diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index c67d2867a3a9..ec218d84183e 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -25,9 +25,6 @@ import com.android.volley.toolbox.ImageLoader; import com.google.gson.Gson; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; import org.wordpress.android.editor.BuildConfig; import org.wordpress.android.editor.EditorEditMediaListener; import org.wordpress.android.editor.EditorFragmentAbstract; @@ -546,6 +543,16 @@ public void onEditorThemeUpdated(Bundle editorTheme) { // Unused, no-op retained for the shared interface with Gutenberg } + public void updateEditorSettings(Bundle editorSettings) { + if (mGutenbergView == null) { + return; + } + + EditorConfiguration config = mGutenbergView.getEditorConfiguration(); + config.editorSettings = editorSettings; + mGutenbergView.setEditorConfiguration(config); + } + @Override public void showNotice(String message) { // Unused, no-op retained for the shared interface with Gutenberg @@ -578,26 +585,4 @@ public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { public void onConnectionStatusChange(boolean isConnected) { // Unused, no-op retained for the shared interface with Gutenberg } - - @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) - public void onEditorSettingsChanged(EditorSettingsStore.OnEditorSettingsChanged event) { - if (mGutenbergView == null || event.editorSettings == null) - return; - - EditorConfiguration config = mGutenbergView.getEditorConfiguration(); - config.editorSettings = event.editorSettings.toBundle(); - mGutenbergView.setEditorConfiguration(config); - } - - @Override - public void onStart() { - super.onStart(); - EventBus.getDefault().register(this); - } - - @Override - public void onStop() { - super.onStop(); - EventBus.getDefault().unregister(this); - } } From 14f99cc38e94f653bd3353f5a191b7b7a5953a4c Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 8 Apr 2025 11:49:46 -0400 Subject: [PATCH 03/22] style: Automated formatting changes --- .../gutenberg/GutenbergKitEditorFragment.java | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index ec218d84183e..d1b3cbcc4fb6 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -68,7 +68,8 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement GutenbergDialogPositiveClickInterface, GutenbergDialogNegativeClickInterface, GutenbergNetworkConnectionListener { - @Nullable private GutenbergView mGutenbergView; + @Nullable + private GutenbergView mGutenbergView; private static final String GUTENBERG_EDITOR_NAME = "gutenberg"; private static final String KEY_HTML_MODE_ENABLED = "KEY_HTML_MODE_ENABLED"; private static final String KEY_EDITOR_DID_MOUNT = "KEY_EDITOR_DID_MOUNT"; @@ -84,19 +85,23 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement private boolean mHtmlModeEnabled; private final LiveTextWatcher mTextWatcher = new LiveTextWatcher(); - @Nullable private HistoryChangeListener mHistoryChangeListener = null; - @Nullable private OpenMediaLibraryListener mOpenMediaLibraryListener = null; - @Nullable private LogJsExceptionListener mOnLogJsExceptionListener = null; + @Nullable + private HistoryChangeListener mHistoryChangeListener = null; + @Nullable + private OpenMediaLibraryListener mOpenMediaLibraryListener = null; + @Nullable + private LogJsExceptionListener mOnLogJsExceptionListener = null; private boolean mEditorDidMount; - @Nullable private static Map mSettings; + @Nullable + private static Map mSettings; public static GutenbergKitEditorFragment newInstance(Context context, - boolean isNewPost, - GutenbergWebViewAuthorizationData webViewAuthorizationData, - boolean jetpackFeaturesEnabled, - @Nullable Map settings) { + boolean isNewPost, + GutenbergWebViewAuthorizationData webViewAuthorizationData, + boolean jetpackFeaturesEnabled, + @Nullable Map settings) { GutenbergKitEditorFragment fragment = new GutenbergKitEditorFragment(); Bundle args = new Bundle(); args.putBoolean(ARG_IS_NEW_POST, isNewPost); @@ -140,8 +145,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); mGutenbergView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - )); + ViewGroup.LayoutParams.MATCH_PARENT)); mGutenbergView.setOnFileChooserRequestedListener((intent, requestCode) -> { startActivityForResult(intent, requestCode); return null; @@ -178,7 +182,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return mGutenbergView; } - @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); if (mGutenbergView != null) { @@ -204,7 +209,7 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d filePathCallback.onReceiveValue(uris); } else if (data.getData() != null) { Uri uri = data.getData(); - filePathCallback.onReceiveValue(new Uri[]{uri}); + filePathCallback.onReceiveValue(new Uri[] { uri }); } else { filePathCallback.onReceiveValue(null); } @@ -216,13 +221,14 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d } } - @Override public void onResume() { + @Override + public void onResume() { super.onResume(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { + @NonNull int[] grantResults) { if (PermissionUtils.checkCameraAndStoragePermissions(this.getActivity())) { if (requestCode == CAPTURE_PHOTO_PERMISSION_REQUEST_CODE) { mEditorFragmentListener.onCapturePhotoClicked(); @@ -335,8 +341,8 @@ private void toggleHtmlMode() { } @Override - public Pair getTitleAndContent(CharSequence originalContent) throws - EditorFragmentNotAddedException { + public Pair getTitleAndContent(CharSequence originalContent) + throws EditorFragmentNotAddedException { final Pair[] result = new Pair[1]; final CountDownLatch latch = new CountDownLatch(1); @@ -370,7 +376,8 @@ public boolean isActionInProgress() { } /** - * Returns the contents of the content field from the JavaScript editor. Should be called from a background thread + * Returns the contents of the content field from the JavaScript editor. Should + * be called from a background thread * where possible. */ @Override @@ -414,7 +421,8 @@ public void appendMediaFile(final MediaFile mediaFile, final String mediaUrl, Im @Override public void appendMediaFiles(Map mediaList) { if (getActivity() == null) { - // appendMediaFile may be called from a background thread (example: EditPostActivity.java#L2165) and + // appendMediaFile may be called from a background thread (example: + // EditPostActivity.java#L2165) and // Activity may have already be gone. // Ticket: https://github.com/wordpress-mobile/WordPress-Android/issues/7386 AppLog.d(T.MEDIA, "appendMediaFiles() called but Activity is null!"); @@ -499,7 +507,8 @@ public void onDestroy() { super.onDestroy(); } - @Override public void mediaSelectionCancelled() { + @Override + public void mediaSelectionCancelled() { // Unused, no-op retained for the shared interface with Gutenberg } @@ -563,11 +572,13 @@ public void showEditorHelp() { // Unused, no-op retained for the shared interface with Gutenberg } - @Override public void onUndoPressed() { + @Override + public void onUndoPressed() { mGutenbergView.undo(); } - @Override public void onRedoPressed() { + @Override + public void onRedoPressed() { mGutenbergView.redo(); } From 0de6ee01b5e762687b90764bf093d2d7ffd62846 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 8 Apr 2025 12:01:58 -0400 Subject: [PATCH 04/22] feat: Dispatch editor settings request --- .../android/ui/posts/EditPostActivity.kt | 26 +++++++++++++++-- .../gutenberg/GutenbergKitEditorFragment.java | 28 +++++++++++++++++-- .../fluxc/store/EditorSettingsStore.kt | 5 ++-- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index b041839f9155..eb67490897e4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -82,6 +82,7 @@ import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase.Companio import org.wordpress.android.fluxc.Dispatcher import org.wordpress.android.fluxc.action.AccountAction import org.wordpress.android.fluxc.generated.AccountActionBuilder +import org.wordpress.android.fluxc.generated.EditorSettingsActionBuilder import org.wordpress.android.fluxc.generated.EditorThemeActionBuilder import org.wordpress.android.fluxc.generated.PostActionBuilder import org.wordpress.android.fluxc.generated.SiteActionBuilder @@ -99,6 +100,9 @@ import org.wordpress.android.fluxc.network.UserAgent import org.wordpress.android.fluxc.network.rest.wpcom.site.PrivateAtomicCookie import org.wordpress.android.fluxc.store.AccountStore import org.wordpress.android.fluxc.store.AccountStore.OnAccountChanged +import org.wordpress.android.fluxc.store.EditorSettingsStore +import org.wordpress.android.fluxc.store.EditorSettingsStore.FetchEditorSettingsPayload +import org.wordpress.android.fluxc.store.EditorSettingsStore.OnEditorSettingsChanged import org.wordpress.android.fluxc.store.EditorThemeStore import org.wordpress.android.fluxc.store.EditorThemeStore.FetchEditorThemePayload import org.wordpress.android.fluxc.store.EditorThemeStore.OnEditorThemeChanged @@ -348,6 +352,8 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor @Inject lateinit var editorThemeStore: EditorThemeStore + @Inject lateinit var editorSettingsStore: EditorSettingsStore + @Inject lateinit var imageLoader: FluxCImageLoader @Inject lateinit var shortcutUtils: ShortcutUtils @@ -3636,9 +3642,12 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor } private fun onEditorFinalTouchesBeforeShowing() { - refreshEditorContent() + if (editorFragment !is GutenbergKitEditorFragment) { + refreshEditorContent() + } onEditorFinalTouchesBeforeShowingForGutenbergIfNeeded() + onEditorFinalTouchesBeforeShowingForGutenbergKitIfNeeded() onEditorFinalTouchesBeforeShowingForAztecIfNeeded() } private fun onEditorFinalTouchesBeforeShowingForGutenbergIfNeeded() { @@ -3661,6 +3670,13 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor (editorFragment as GutenbergEditorFragment).resetUploadingMediaToFailed(mediaIds) } } + + private fun onEditorFinalTouchesBeforeShowingForGutenbergKitIfNeeded() { + if (showGutenbergEditor && editorFragment is GutenbergKitEditorFragment) { + refreshEditorSettings() + } + } + private fun onEditorFinalTouchesBeforeShowingForAztecIfNeeded() { if (showAztecEditor && editorFragment is AztecEditorFragment) { val entryPoint = @@ -4065,8 +4081,14 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor postEditorAnalyticsSession?.editorSettingsFetched(editorThemeSupport.isBlockBasedTheme, event.endpoint.value) } + private fun refreshEditorSettings() { + val payload = FetchEditorSettingsPayload(siteModel) + dispatcher.dispatch(EditorSettingsActionBuilder.newFetchEditorSettingsAction(payload)) + } + + @Suppress("unused") @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) - fun onEditorSettingsChanged(event: EditorSettingsStore.OnEditorSettingsChanged) { + fun onEditorSettingsChanged(event: OnEditorSettingsChanged) { if (site.id != event.siteId) return val editorSettings = event.editorSettings ?: return diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index d1b3cbcc4fb6..1782e5cf4c0f 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -55,6 +55,7 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -142,6 +143,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa ((EditorFragmentActivity) getActivity()).initializeEditorFragment(); } + mEditorFragmentListener.onEditorFragmentInitialized(); + mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); mGutenbergView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -557,9 +560,28 @@ public void updateEditorSettings(Bundle editorSettings) { return; } - EditorConfiguration config = mGutenbergView.getEditorConfiguration(); - config.editorSettings = editorSettings; - mGutenbergView.setEditorConfiguration(config); + Map settingsMap = new HashMap<>(); + for (String key : editorSettings.keySet()) { + Object value = editorSettings.get(key); + settingsMap.put(key, value); + } + + EditorConfiguration config = new EditorConfiguration.Builder() + .setTitle((String) mSettings.get("postTitle")) + .setContent((String) mSettings.get("postContent")) + .setPostId((Integer) mSettings.get("postId")) + .setPostType((String) mSettings.get("postType")) + .setThemeStyles((Boolean) mSettings.get("themeStyles")) + .setPlugins((Boolean) mSettings.get("plugins")) + .setSiteApiRoot((String) mSettings.get("siteApiRoot")) + .setSiteApiNamespace((String[]) mSettings.get("siteApiNamespace")) + .setNamespaceExcludedPaths((String[]) mSettings.get("namespaceExcludedPaths")) + .setAuthHeader((String) mSettings.get("authHeader")) + .setWebViewGlobals((List) mSettings.get("webViewGlobals")) + .setEditorSettings(settingsMap) + .build(); + + mGutenbergView.start(config); } @Override diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt index df22276be45e..3ed8399f6762 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt @@ -18,7 +18,7 @@ import org.wordpress.android.util.AppLog import javax.inject.Inject import javax.inject.Singleton -private const val EDITOR_SETTINGS_REQUEST_PATH = "wp-block-editor/v1/settings?context=mobile" +private const val EDITOR_SETTINGS_REQUEST_PATH = "wp-block-editor/v1/settings" @Singleton class EditorSettingsStore @Inject constructor( @@ -75,8 +75,7 @@ class EditorSettingsStore @Inject constructor( return } - val editorSettings = Gson().fromJson(response.result, EditorSettings::class.java) - editorSettings.rawSettings = response.result.asJsonObject + val editorSettings = EditorSettings(response.result.asJsonObject) val onChanged = OnEditorSettingsChanged(editorSettings, site.id, action) emitChange(onChanged) } From 00438a30107e8760456f1f09d8bf344ac5428aa9 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 8 Apr 2025 13:52:45 -0400 Subject: [PATCH 05/22] refactor: Improve editor settings parsing --- .../android/fluxc/model/EditorSettings.kt | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt index 26e034f6c98e..ea39ed17e947 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -2,61 +2,61 @@ package org.wordpress.android.fluxc.model import android.os.Bundle import com.google.gson.JsonObject -import com.google.gson.annotations.SerializedName +import com.google.gson.JsonArray +import com.google.gson.JsonPrimitive data class EditorSettings( - @SerializedName("colors") val colors: List? = null, - @SerializedName("gradients") val gradients: List? = null, - @SerializedName("styles") val styles: String? = null, - @SerializedName("features") val features: String? = null, - @SerializedName("isBlockBasedTheme") val isBlockBasedTheme: Boolean = false, - @SerializedName("galleryWithImageBlocks") val galleryWithImageBlocks: Boolean = false, - @SerializedName("quoteBlockV2") val quoteBlockV2: Boolean = false, - @SerializedName("listBlockV2") val listBlockV2: Boolean = false, - @SerializedName("hasBlockTemplates") val hasBlockTemplates: Boolean = false, - val rawSettings: JsonObject? = null + val settings: JsonObject ) { fun toBundle(): Bundle { val bundle = Bundle() + processJsonObject(settings, bundle) + return bundle + } - colors?.map { it.toBundle() }?.let { - bundle.putParcelableArrayList("colors", ArrayList(it)) + private fun processJsonObject(jsonObject: JsonObject, bundle: Bundle) { + jsonObject.entrySet().forEach { entry -> + when (val value = entry.value) { + is JsonPrimitive -> processJsonPrimitive(entry.key, value, bundle) + is JsonArray -> processJsonArray(entry.key, value, bundle) + is JsonObject -> { + val nestedBundle = Bundle() + processJsonObject(value, nestedBundle) + bundle.putBundle(entry.key, nestedBundle) + } + } } + } - gradients?.map { it.toBundle() }?.let { - bundle.putParcelableArrayList("gradients", ArrayList(it)) + private fun processJsonPrimitive(key: String, value: JsonPrimitive, bundle: Bundle) { + when { + value.isBoolean -> bundle.putBoolean(key, value.asBoolean) + value.isNumber -> bundle.putDouble(key, value.asDouble) + value.isString -> bundle.putString(key, value.asString) } + } - styles?.let { bundle.putString("styles", it) } - features?.let { bundle.putString("features", it) } - bundle.putBoolean("isBlockBasedTheme", isBlockBasedTheme) - bundle.putBoolean("galleryWithImageBlocks", galleryWithImageBlocks) - bundle.putBoolean("quoteBlockV2", quoteBlockV2) - bundle.putBoolean("listBlockV2", listBlockV2) - bundle.putBoolean("hasBlockTemplates", hasBlockTemplates) - - rawSettings?.let { json -> - json.entrySet().forEach { entry -> - when (val value = entry.value) { - is com.google.gson.JsonPrimitive -> { - if (value.isBoolean) { - bundle.putBoolean(entry.key, value.asBoolean) - } else if (value.isNumber) { - bundle.putDouble(entry.key, value.asDouble) - } else if (value.isString) { - bundle.putString(entry.key, value.asString) - } - } - is com.google.gson.JsonArray -> { - // Handle arrays if needed - } - is com.google.gson.JsonObject -> { - // Handle nested objects if needed - } + private fun processJsonArray(key: String, array: JsonArray, bundle: Bundle) { + val arrayList = ArrayList() + array.forEach { element -> + when (element) { + is JsonObject -> { + val nestedBundle = Bundle() + processJsonObject(element, nestedBundle) + arrayList.add(nestedBundle) + } + is JsonPrimitive -> { + val primitiveBundle = Bundle() + processJsonPrimitive("value", element, primitiveBundle) + arrayList.add(primitiveBundle) + } + is JsonArray -> { + val arrayBundle = Bundle() + processJsonArray("nested", element, arrayBundle) + arrayList.add(arrayBundle) } } } - - return bundle + bundle.putParcelableArrayList(key, arrayList) } } From a9710cbcea856863f94a92f80c52262193a0c096 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 8 Apr 2025 14:54:43 -0400 Subject: [PATCH 06/22] refactor: Simplify EditorSettings --- .../android/fluxc/model/EditorSettings.kt | 57 ++++--------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt index ea39ed17e947..9ebd664d4d80 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -2,61 +2,24 @@ package org.wordpress.android.fluxc.model import android.os.Bundle import com.google.gson.JsonObject -import com.google.gson.JsonArray -import com.google.gson.JsonPrimitive +import com.google.gson.JsonParser +import org.json.JSONObject data class EditorSettings( val settings: JsonObject ) { - fun toBundle(): Bundle { - val bundle = Bundle() - processJsonObject(settings, bundle) - return bundle - } - - private fun processJsonObject(jsonObject: JsonObject, bundle: Bundle) { - jsonObject.entrySet().forEach { entry -> - when (val value = entry.value) { - is JsonPrimitive -> processJsonPrimitive(entry.key, value, bundle) - is JsonArray -> processJsonArray(entry.key, value, bundle) - is JsonObject -> { - val nestedBundle = Bundle() - processJsonObject(value, nestedBundle) - bundle.putBundle(entry.key, nestedBundle) - } - } + companion object { + fun fromJsonString(jsonString: String): EditorSettings { + val jsonObject = JsonParser.parseString(jsonString).asJsonObject + return EditorSettings(jsonObject) } } - private fun processJsonPrimitive(key: String, value: JsonPrimitive, bundle: Bundle) { - when { - value.isBoolean -> bundle.putBoolean(key, value.asBoolean) - value.isNumber -> bundle.putDouble(key, value.asDouble) - value.isString -> bundle.putString(key, value.asString) - } + fun toJsonString(): String { + return settings.toString() } - private fun processJsonArray(key: String, array: JsonArray, bundle: Bundle) { - val arrayList = ArrayList() - array.forEach { element -> - when (element) { - is JsonObject -> { - val nestedBundle = Bundle() - processJsonObject(element, nestedBundle) - arrayList.add(nestedBundle) - } - is JsonPrimitive -> { - val primitiveBundle = Bundle() - processJsonPrimitive("value", element, primitiveBundle) - arrayList.add(primitiveBundle) - } - is JsonArray -> { - val arrayBundle = Bundle() - processJsonArray("nested", element, arrayBundle) - arrayList.add(arrayBundle) - } - } - } - bundle.putParcelableArrayList(key, arrayList) + fun toJSONObject(): JSONObject { + return JSONObject(settings.toString()) } } From 7ae1aa59fbf24e8efb133a3acd758be5cca6a1b3 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 8 Apr 2025 17:33:49 -0400 Subject: [PATCH 07/22] refactor: Pass editor settings as JSON string Avoid typing complex JSON structures. --- .../wordpress/android/ui/posts/EditPostActivity.kt | 2 +- .../gutenberg/GutenbergKitEditorFragment.java | 10 ++-------- .../wordpress/android/fluxc/model/EditorSettings.kt | 13 ------------- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index eb67490897e4..46aef9ced1bf 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -4092,7 +4092,7 @@ class EditPostActivity : BaseAppCompatActivity(), EditorFragmentActivity, Editor if (site.id != event.siteId) return val editorSettings = event.editorSettings ?: return - (editorFragment as? GutenbergKitEditorFragment)?.updateEditorSettings(editorSettings.toBundle()) + (editorFragment as? GutenbergKitEditorFragment)?.updateEditorSettings(editorSettings.toJsonString()) } // EditPostActivityHook methods diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index 1782e5cf4c0f..582d1db629f2 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -555,17 +555,11 @@ public void onEditorThemeUpdated(Bundle editorTheme) { // Unused, no-op retained for the shared interface with Gutenberg } - public void updateEditorSettings(Bundle editorSettings) { + public void updateEditorSettings(String editorSettings) { if (mGutenbergView == null) { return; } - Map settingsMap = new HashMap<>(); - for (String key : editorSettings.keySet()) { - Object value = editorSettings.get(key); - settingsMap.put(key, value); - } - EditorConfiguration config = new EditorConfiguration.Builder() .setTitle((String) mSettings.get("postTitle")) .setContent((String) mSettings.get("postContent")) @@ -578,7 +572,7 @@ public void updateEditorSettings(Bundle editorSettings) { .setNamespaceExcludedPaths((String[]) mSettings.get("namespaceExcludedPaths")) .setAuthHeader((String) mSettings.get("authHeader")) .setWebViewGlobals((List) mSettings.get("webViewGlobals")) - .setEditorSettings(settingsMap) + .setEditorSettings(editorSettings) .build(); mGutenbergView.start(config); diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt index 9ebd664d4d80..8c01b118cc3f 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -1,25 +1,12 @@ package org.wordpress.android.fluxc.model -import android.os.Bundle import com.google.gson.JsonObject import com.google.gson.JsonParser -import org.json.JSONObject data class EditorSettings( val settings: JsonObject ) { - companion object { - fun fromJsonString(jsonString: String): EditorSettings { - val jsonObject = JsonParser.parseString(jsonString).asJsonObject - return EditorSettings(jsonObject) - } - } - fun toJsonString(): String { return settings.toString() } - - fun toJSONObject(): JSONObject { - return JSONObject(settings.toString()) - } } From 37df679eacc767a305fae708b86de2620ff37292 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 11:48:52 -0400 Subject: [PATCH 08/22] feat: Display activity indicator while fetching editor settings Communicate active background work to the user. --- .../gutenberg/GutenbergKitEditorFragment.java | 19 ++++++++++++++++++- .../layout/fragment_gutenberg_kit_editor.xml | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index 582d1db629f2..d2163f85bf61 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -94,6 +94,7 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement private LogJsExceptionListener mOnLogJsExceptionListener = null; private boolean mEditorDidMount; + private View mRootView; @Nullable private static Map mSettings; @@ -145,10 +146,17 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mEditorFragmentListener.onEditorFragmentInitialized(); + mRootView = inflater.inflate(R.layout.fragment_gutenberg_kit_editor, container, false); + ViewGroup gutenbergViewContainer = mRootView.findViewById(R.id.gutenberg_view_container); + mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); mGutenbergView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + gutenbergViewContainer.addView(mGutenbergView); + + setEditorProgressBarVisibility(true); + mGutenbergView.setOnFileChooserRequestedListener((intent, requestCode) -> { startActivityForResult(intent, requestCode); return null; @@ -158,7 +166,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mGutenbergView.setOpenMediaLibraryListener(mOpenMediaLibraryListener); mGutenbergView.setLogJsExceptionListener(mOnLogJsExceptionListener); mGutenbergView.setEditorDidBecomeAvailable(view -> { + mEditorDidMount = true; mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList<>(), false); + setEditorProgressBarVisibility(false); }); Integer postId = (Integer) mSettings.get("postId"); @@ -182,7 +192,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mGutenbergView.start(config); - return mGutenbergView; + return mRootView; } @Override @@ -227,6 +237,13 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d @Override public void onResume() { super.onResume(); + setEditorProgressBarVisibility(!mEditorDidMount); + } + + private void setEditorProgressBarVisibility(boolean shown) { + if (isAdded() && mRootView != null) { + mRootView.findViewById(R.id.editor_progress).setVisibility(shown ? View.VISIBLE : View.GONE); + } } @Override diff --git a/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml b/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml new file mode 100644 index 000000000000..47e42ceeef4f --- /dev/null +++ b/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml @@ -0,0 +1,19 @@ + + + + + + + From dd7a59dfd8e4f4ed67a823e21beeb96d3bcd9c66 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 11:56:24 -0400 Subject: [PATCH 09/22] feat: Add cache-while-revalidate for editor settings Improve offline support and performance. --- .../fluxc/store/EditorSettingsStore.kt | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt index 3ed8399f6762..7d8c13c02831 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt @@ -26,12 +26,16 @@ class EditorSettingsStore @Inject constructor( private val coroutineEngine: CoroutineEngine, dispatcher: Dispatcher ) : Store(dispatcher) { + // Cache to store editor settings by site ID + private val editorSettingsCache = mutableMapOf() + class FetchEditorSettingsPayload(val site: SiteModel) : Payload() data class OnEditorSettingsChanged( val editorSettings: EditorSettings?, val siteId: Int, - val causeOfChange: EditorSettingsAction + val causeOfChange: EditorSettingsAction, + val isFromCache: Boolean = false ) : Store.OnChanged() { constructor(error: EditorSettingsError, causeOfChange: EditorSettingsAction) : this(editorSettings = null, siteId = -1, causeOfChange = causeOfChange) { @@ -63,6 +67,13 @@ class EditorSettingsStore @Inject constructor( } private suspend fun fetchEditorSettings(site: SiteModel, action: EditorSettingsAction) { + // First emit cached data if available + val cachedSettings = editorSettingsCache[site.id] + if (cachedSettings != null) { + emitChange(OnEditorSettingsChanged(cachedSettings, site.id, action, isFromCache = true)) + } + + // Then fetch fresh data val response = reactNativeStore.executeGetRequest(site, EDITOR_SETTINGS_REQUEST_PATH, false) when (response) { @@ -76,8 +87,14 @@ class EditorSettingsStore @Inject constructor( } val editorSettings = EditorSettings(response.result.asJsonObject) - val onChanged = OnEditorSettingsChanged(editorSettings, site.id, action) - emitChange(onChanged) + // Update cache + editorSettingsCache[site.id] = editorSettings + + // Only emit change if the data is different from cache + if (cachedSettings != editorSettings) { + val onChanged = OnEditorSettingsChanged(editorSettings, site.id, action) + emitChange(onChanged) + } } is Error -> { val onChanged = OnEditorSettingsChanged( From 2e0a1dc89b5a17d603d1b99a3cd2e7b5aa2a9ad1 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 14:36:05 -0400 Subject: [PATCH 10/22] feat: Persist editor settings in SQLite Match caching strategy for other FluxC stores. --- .../android/fluxc/model/EditorSettings.kt | 14 ++-- .../persistence/EditorSettingsSqlUtils.kt | 66 +++++++++++++++++++ .../fluxc/persistence/WellSqlConfig.kt | 13 +++- .../fluxc/store/EditorSettingsStore.kt | 8 +-- 4 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/EditorSettingsSqlUtils.kt diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt index 8c01b118cc3f..e572c2bfbaf8 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -2,11 +2,17 @@ package org.wordpress.android.fluxc.model import com.google.gson.JsonObject import com.google.gson.JsonParser +import org.wordpress.android.fluxc.persistence.EditorSettingsSqlUtils.EditorSettingsBuilder -data class EditorSettings( - val settings: JsonObject -) { +class EditorSettings(val rawSettings: JsonObject) { fun toJsonString(): String { - return settings.toString() + return rawSettings.toString() + } + + fun toBuilder(site: SiteModel): EditorSettingsBuilder { + return EditorSettingsBuilder().apply { + localSiteId = site.id + rawSettings = this@EditorSettings.rawSettings.toString() + } } } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/EditorSettingsSqlUtils.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/EditorSettingsSqlUtils.kt new file mode 100644 index 000000000000..bbfa54580f09 --- /dev/null +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/EditorSettingsSqlUtils.kt @@ -0,0 +1,66 @@ +package org.wordpress.android.fluxc.persistence + +import com.google.gson.JsonParser +import com.wellsql.generated.EditorSettingsTable +import com.yarolegovich.wellsql.WellSql +import com.yarolegovich.wellsql.core.Identifiable +import com.yarolegovich.wellsql.core.annotation.Column +import com.yarolegovich.wellsql.core.annotation.PrimaryKey +import com.yarolegovich.wellsql.core.annotation.Table +import org.wordpress.android.fluxc.model.EditorSettings +import org.wordpress.android.fluxc.model.SiteModel + +class EditorSettingsSqlUtils { + fun replaceEditorSettingsForSite(site: SiteModel, editorSettings: EditorSettings?) { + deleteEditorSettingsForSite(site) + if (editorSettings == null) return + makeEditorSettings(site, editorSettings) + } + + fun getEditorSettingsForSite(site: SiteModel): EditorSettings? { + return WellSql.select(EditorSettingsBuilder::class.java) + .limit(1) + .where() + .equals(EditorSettingsTable.LOCAL_SITE_ID, site.id) + .endWhere() + .asModel + .firstOrNull() + ?.toEditorSettings() + } + + fun deleteEditorSettingsForSite(site: SiteModel) { + WellSql.delete(EditorSettingsBuilder::class.java) + .where() + .equals(EditorSettingsTable.LOCAL_SITE_ID, site.id) + .endWhere() + .execute() + } + + private fun makeEditorSettings(site: SiteModel, editorSettings: EditorSettings) { + val builder = editorSettings.toBuilder(site) + WellSql.insert(builder).execute() + } + + @Table(name = "EditorSettings") + data class EditorSettingsBuilder(@PrimaryKey @Column private var mId: Int = -1) : Identifiable { + @Column var localSiteId: Int = -1 + @JvmName("getLocalSiteId") + get + @JvmName("setLocalSiteId") + set + @Column var rawSettings: String? = null + + override fun setId(id: Int) { + this.mId = id + } + + override fun getId() = mId + + fun toEditorSettings(): EditorSettings? { + return rawSettings?.let { + val jsonObject = JsonParser.parseString(it).asJsonObject + EditorSettings(jsonObject) + } + } + } +} diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt index 02fd550c8b76..96459c416134 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/persistence/WellSqlConfig.kt @@ -41,7 +41,7 @@ open class WellSqlConfig : DefaultWellConfig { annotation class AddOn override fun getDbVersion(): Int { - return 205 + return 206 } override fun getDbName(): String { @@ -2055,6 +2055,17 @@ open class WellSqlConfig : DefaultWellConfig { } 204 -> db.execSQL("ALTER TABLE SiteModel ADD IS_DELETED INTEGER DEFAULT 0") + + 205 -> migrate(version) { + db.execSQL(""" + CREATE TABLE IF NOT EXISTS EditorSettings ( + _id INTEGER PRIMARY KEY AUTOINCREMENT, + LOCAL_SITE_ID INTEGER NOT NULL, + RAW_SETTINGS TEXT, + FOREIGN KEY (LOCAL_SITE_ID) REFERENCES SiteModel(_id) ON DELETE CASCADE + ) + """.trimIndent()) + } } } db.setTransactionSuccessful() diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt index 7d8c13c02831..fd81cfd30fb8 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt @@ -11,6 +11,7 @@ import org.wordpress.android.fluxc.annotations.action.Action import org.wordpress.android.fluxc.model.EditorSettings import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError +import org.wordpress.android.fluxc.persistence.EditorSettingsSqlUtils import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Error import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Success import org.wordpress.android.fluxc.tools.CoroutineEngine @@ -26,8 +27,7 @@ class EditorSettingsStore @Inject constructor( private val coroutineEngine: CoroutineEngine, dispatcher: Dispatcher ) : Store(dispatcher) { - // Cache to store editor settings by site ID - private val editorSettingsCache = mutableMapOf() + private val editorSettingsSqlUtils = EditorSettingsSqlUtils() class FetchEditorSettingsPayload(val site: SiteModel) : Payload() @@ -68,7 +68,7 @@ class EditorSettingsStore @Inject constructor( private suspend fun fetchEditorSettings(site: SiteModel, action: EditorSettingsAction) { // First emit cached data if available - val cachedSettings = editorSettingsCache[site.id] + val cachedSettings = editorSettingsSqlUtils.getEditorSettingsForSite(site) if (cachedSettings != null) { emitChange(OnEditorSettingsChanged(cachedSettings, site.id, action, isFromCache = true)) } @@ -88,7 +88,7 @@ class EditorSettingsStore @Inject constructor( val editorSettings = EditorSettings(response.result.asJsonObject) // Update cache - editorSettingsCache[site.id] = editorSettings + editorSettingsSqlUtils.replaceEditorSettingsForSite(site, editorSettings) // Only emit change if the data is different from cache if (cachedSettings != editorSettings) { From 6ea9561dd11683e0ee4237505add5dee6ca8194d Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 15:08:53 -0400 Subject: [PATCH 11/22] build: Update GutenbergKit ref --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dbe830e2a172..c7199fa24de0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -72,7 +72,7 @@ google-play-services-auth = '20.4.1' google-services = '4.4.2' gravatar = '2.4.1' greenrobot-eventbus = '3.3.1' -gutenberg-kit = 'trunk-a03e0dae10a404c88c215bfcee3176df951302f5' +gutenberg-kit = '114-9092f64e3346a998ed652def5efef01b8b3b84f6' gutenberg-mobile = 'v1.121.0' indexos-media-for-mobile = '43a9026f0973a2f0a74fa813132f6a16f7499c3a' jackson-databind = '2.12.7.1' From 1434de3ea584316d1e7027de224e0237a69ed96f Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 16:13:04 -0400 Subject: [PATCH 12/22] style: Address lint errors --- .../android/editor/gutenberg/GutenbergKitEditorFragment.java | 3 +-- .../org/wordpress/android/fluxc/action/EditorSettingsAction.kt | 1 - .../java/org/wordpress/android/fluxc/model/EditorSettings.kt | 1 - .../org/wordpress/android/fluxc/store/EditorSettingsStore.kt | 1 - 4 files changed, 1 insertion(+), 5 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index d2163f85bf61..9cbd07a7a260 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -55,7 +55,6 @@ import java.io.Serializable; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @@ -222,7 +221,7 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d filePathCallback.onReceiveValue(uris); } else if (data.getData() != null) { Uri uri = data.getData(); - filePathCallback.onReceiveValue(new Uri[] { uri }); + filePathCallback.onReceiveValue(new Uri[] {uri}); } else { filePathCallback.onReceiveValue(null); } diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt index 66f18e460485..c857d38d6a1f 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/action/EditorSettingsAction.kt @@ -3,7 +3,6 @@ package org.wordpress.android.fluxc.action import org.wordpress.android.fluxc.annotations.Action import org.wordpress.android.fluxc.annotations.ActionEnum import org.wordpress.android.fluxc.annotations.action.IAction -import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.EditorSettingsStore.FetchEditorSettingsPayload @ActionEnum diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt index e572c2bfbaf8..e7818dcdaafc 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/model/EditorSettings.kt @@ -1,7 +1,6 @@ package org.wordpress.android.fluxc.model import com.google.gson.JsonObject -import com.google.gson.JsonParser import org.wordpress.android.fluxc.persistence.EditorSettingsSqlUtils.EditorSettingsBuilder class EditorSettings(val rawSettings: JsonObject) { diff --git a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt index fd81cfd30fb8..13ae98f9d69c 100644 --- a/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt +++ b/libs/fluxc/src/main/java/org/wordpress/android/fluxc/store/EditorSettingsStore.kt @@ -1,6 +1,5 @@ package org.wordpress.android.fluxc.store -import com.google.gson.Gson import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.wordpress.android.fluxc.Dispatcher From acad23be30b86e028c2c71f09a14cb748069fa19 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Wed, 9 Apr 2025 17:09:27 -0400 Subject: [PATCH 13/22] style: Address lint warnings --- .../android/editor/gutenberg/GutenbergKitEditorFragment.java | 5 +++-- .../src/main/res/layout/fragment_gutenberg_kit_editor.xml | 3 +-- libs/editor/src/main/res/values/styles.xml | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index 9cbd07a7a260..71cf05f9bc7e 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -93,6 +93,7 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement private LogJsExceptionListener mOnLogJsExceptionListener = null; private boolean mEditorDidMount; + @Nullable private View mRootView; @Nullable @@ -100,7 +101,7 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement public static GutenbergKitEditorFragment newInstance(Context context, boolean isNewPost, - GutenbergWebViewAuthorizationData webViewAuthorizationData, + @Nullable GutenbergWebViewAuthorizationData webViewAuthorizationData, boolean jetpackFeaturesEnabled, @Nullable Map settings) { GutenbergKitEditorFragment fragment = new GutenbergKitEditorFragment(); @@ -571,7 +572,7 @@ public void onEditorThemeUpdated(Bundle editorTheme) { // Unused, no-op retained for the shared interface with Gutenberg } - public void updateEditorSettings(String editorSettings) { + public void updateEditorSettings(@NonNull String editorSettings) { if (mGutenbergView == null) { return; } diff --git a/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml b/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml index 47e42ceeef4f..d20f0298540a 100644 --- a/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml +++ b/libs/editor/src/main/res/layout/fragment_gutenberg_kit_editor.xml @@ -2,8 +2,7 @@ + android:layout_width="match_parent"> @color/accent_color + @null