diff --git a/ios/Demo-iOS/Gutenberg.xcodeproj/xcshareddata/xcschemes/Gutenberg.xcscheme b/ios/Demo-iOS/Gutenberg.xcodeproj/xcshareddata/xcschemes/Gutenberg.xcscheme index 1a352dfa..6ae6d110 100644 --- a/ios/Demo-iOS/Gutenberg.xcodeproj/xcshareddata/xcschemes/Gutenberg.xcscheme +++ b/ios/Demo-iOS/Gutenberg.xcodeproj/xcshareddata/xcschemes/Gutenberg.xcscheme @@ -54,7 +54,7 @@ + isEnabled = "YES"> EditorConfiguration { + configuration + .toBuilder() + .setNativeInserterEnabled(isNativeInserterEnabled) + .build() + } +} + +private struct RemoteEditorRow: Identifiable { + let id: String + let configuration: EditorConfiguration + + var title: String { + URL(string: configuration.siteURL)?.host ?? configuration.siteURL + } } private extension EditorConfiguration { static var template: Self { - #warning("1. Update the siteURL and authHeader values below") - #warning("2. Install the Jetpack plugin to the site") + // Steps: + // 1. Update the siteURL and authHeader values below + // 2. Install the Jetpack plugin to the site let siteUrl: String = "https://modify-me.com" let authHeader: String = "Insert the Authorization header value here" let siteApiRoot: String = "\(siteUrl)/wp-json/" diff --git a/ios/Demo-iOS/Sources/EditorView.swift b/ios/Demo-iOS/Sources/EditorView.swift index 6ac49af3..8bf12f6c 100644 --- a/ios/Demo-iOS/Sources/EditorView.swift +++ b/ios/Demo-iOS/Sources/EditorView.swift @@ -5,7 +5,6 @@ struct EditorView: View { private let configuration: EditorConfiguration @State private var viewModel = EditorViewModel() - @State private var editorViewController: EditorViewController? @Environment(\.dismiss) private var dismiss @@ -15,7 +14,6 @@ struct EditorView: View { var body: some View { _EditorView(configuration: configuration, viewModel: viewModel) - .navigationBarBackButtonHidden(true) .toolbar { toolbar } } diff --git a/ios/Sources/GutenbergKit/Sources/EditorConfiguration.swift b/ios/Sources/GutenbergKit/Sources/EditorConfiguration.swift index 3284af0b..985715e5 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorConfiguration.swift +++ b/ios/Sources/GutenbergKit/Sources/EditorConfiguration.swift @@ -30,6 +30,8 @@ public struct EditorConfiguration: Sendable { public let editorSettings: String /// Locale used for translations public let locale: String + /// Enables the native inserter UI in the editor + public let isNativeInserterEnabled: Bool /// Endpoint for loading editor assets, used when enabling `shouldUsePlugins` public var editorAssetsEndpoint: URL? @@ -49,6 +51,7 @@ public struct EditorConfiguration: Sendable { authHeader: String, editorSettings: String, locale: String, + isNativeInserterEnabled: Bool, editorAssetsEndpoint: URL? = nil, ) { self.title = title @@ -65,6 +68,7 @@ public struct EditorConfiguration: Sendable { self.authHeader = authHeader self.editorSettings = editorSettings self.locale = locale + self.isNativeInserterEnabled = isNativeInserterEnabled self.editorAssetsEndpoint = editorAssetsEndpoint } @@ -84,6 +88,7 @@ public struct EditorConfiguration: Sendable { authHeader: authHeader, editorSettings: editorSettings, locale: locale, + isNativeInserterEnabled: isNativeInserterEnabled, editorAssetsEndpoint: editorAssetsEndpoint ) } @@ -114,6 +119,7 @@ public struct EditorConfigurationBuilder { private var authHeader: String private var editorSettings: String private var locale: String + private var isNativeInserterEnabled: Bool private var editorAssetsEndpoint: URL? public init( @@ -131,6 +137,7 @@ public struct EditorConfigurationBuilder { authHeader: String = "", editorSettings: String = "undefined", locale: String = "en", + isNativeInserterEnabled: Bool = false, editorAssetsEndpoint: URL? = nil ){ self.title = title @@ -147,6 +154,7 @@ public struct EditorConfigurationBuilder { self.authHeader = authHeader self.editorSettings = editorSettings self.locale = locale + self.isNativeInserterEnabled = isNativeInserterEnabled self.editorAssetsEndpoint = editorAssetsEndpoint } @@ -234,6 +242,12 @@ public struct EditorConfigurationBuilder { return copy } + public func setNativeInserterEnabled(_ isNativeInserterEnabled: Bool = true) -> EditorConfigurationBuilder { + var copy = self + copy.isNativeInserterEnabled = isNativeInserterEnabled + return copy + } + public func setEditorAssetsEndpoint(_ editorAssetsEndpoint: URL?) -> EditorConfigurationBuilder { var copy = self copy.editorAssetsEndpoint = editorAssetsEndpoint @@ -278,6 +292,7 @@ public struct EditorConfigurationBuilder { authHeader: authHeader, editorSettings: editorSettings, locale: locale, + isNativeInserterEnabled: isNativeInserterEnabled, editorAssetsEndpoint: editorAssetsEndpoint ) } @@ -296,3 +311,4 @@ private extension String { .replacingOccurrences(of: "\u{12}", with: "\\f") } } + diff --git a/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift b/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift index 7c6a9cf1..febb2dce 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift +++ b/ios/Sources/GutenbergKit/Sources/EditorJSMessage.swift @@ -34,7 +34,7 @@ struct EditorJSMessage { /// The editor logged an exception. case onEditorExceptionLogged /// The user tapped the inserter button. - case showBlockPicker + case showBlockInserter /// User requested the Media Library. case openMediaLibrary /// The user triggered an autocompleter. @@ -65,4 +65,8 @@ struct EditorJSMessage { struct ModalDialogBody: Decodable { let dialogType: String } + + struct ShowBlockInserterBody: Decodable { + let blocks: [EditorBlock] + } } diff --git a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift index 25392ba0..297ce436 100644 --- a/ios/Sources/GutenbergKit/Sources/EditorViewController.swift +++ b/ios/Sources/GutenbergKit/Sources/EditorViewController.swift @@ -155,6 +155,7 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro namespaceExcludedPaths: \(Array(configuration.namespaceExcludedPaths)), authHeader: '\(configuration.authHeader)', themeStyles: \(configuration.shouldUseThemeStyles), + enableNativeBlockInserter: \(configuration.isNativeInserterEnabled), hideTitle: \(configuration.shouldHideTitle), editorSettings: \(configuration.editorSettings), locale: '\(configuration.locale)', @@ -265,14 +266,12 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro // MARK: - Internal (Block Inserter) - // TODO: wire with JS and pass blocks - private func showBlockInserter() { -// let viewModel = EditorBlockPickerViewModel(blockTypes: service.blockTypes) -// let view = NavigationView { -// EditorBlockPicker(viewModel: viewModel) -// } -// let host = UIHostingController(rootView: view) -// present(host, animated: true) + private func showBlockInserter(blocks: [EditorBlock]) { + present(UIHostingController(rootView: NavigationView { + List(blocks) { + Text($0.name) + } + }), animated: true) } private func openMediaLibrary(_ config: OpenMediaLibraryAction) { @@ -315,8 +314,9 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro return } delegate?.editor(self, didLogException: editorException) - case .showBlockPicker: - showBlockInserter() + case .showBlockInserter: + let body = try message.decode(EditorJSMessage.ShowBlockInserterBody.self) + showBlockInserter(blocks: body.blocks) case .openMediaLibrary: let config = try message.decode(OpenMediaLibraryAction.self) openMediaLibrary(config) diff --git a/src/components/editor-toolbar/index.jsx b/src/components/editor-toolbar/index.jsx index a606be03..7c73eca8 100644 --- a/src/components/editor-toolbar/index.jsx +++ b/src/components/editor-toolbar/index.jsx @@ -17,7 +17,7 @@ import { ToolbarButton, } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { close, cog } from '@wordpress/icons'; +import { close, cog, plus } from '@wordpress/icons'; import clsx from 'clsx'; import { store as editorStore } from '@wordpress/editor'; @@ -27,6 +27,7 @@ import { store as editorStore } from '@wordpress/editor'; import './style.scss'; import { useModalize } from './use-modalize'; import { useModalDialogState } from '../editor/use-modal-dialog-state'; +import { showBlockInserter, getGBKit } from '../../utils/bridge'; /** * Renders the editor toolbar containing block-related actions. @@ -36,6 +37,8 @@ import { useModalDialogState } from '../editor/use-modal-dialog-state'; * @return {JSX.Element} The rendered editor toolbar component. */ const EditorToolbar = ( { className } ) => { + const { enableNativeBlockInserter } = getGBKit(); + const [ isBlockInspectorShown, setBlockInspectorShown ] = useState( false ); const { isSelected } = useSelect( ( select ) => { const { getSelectedBlockClientId } = select( blockEditorStore ); @@ -77,6 +80,29 @@ const EditorToolbar = ( { className } ) => { const classes = clsx( 'gutenberg-kit-editor-toolbar', className ); + const addBlockButton = enableNativeBlockInserter ? ( + { + if ( isInserterOpened ) { + setIsInserterOpened( false ); + } + showBlockInserter(); + } } + className="gutenberg-kit-add-block-button" + /> + ) : ( + + ); + return ( <> { label="Editor toolbar" variant="unstyled" > - - - + { addBlockButton } { isSelected && ( diff --git a/src/components/editor-toolbar/style.scss b/src/components/editor-toolbar/style.scss index c7d49c8e..311dfc32 100644 --- a/src/components/editor-toolbar/style.scss +++ b/src/components/editor-toolbar/style.scss @@ -71,3 +71,19 @@ $min-touch-target-size: 46px; left: 6px; right: 6px; } + +// Style the add block button with rounded black background +.gutenberg-kit-editor-toolbar .gutenberg-kit-add-block-button { + svg { + background: #eae9ec; + border-radius: 18px; + color: wordpress.$black; + padding: 1px; + width: 32px; + height: 32px; + display: block; + } + + // width: 50px; + margin-left: 8px; +} \ No newline at end of file diff --git a/src/utils/bridge.js b/src/utils/bridge.js index c0bb71f0..ce9f36af 100644 --- a/src/utils/bridge.js +++ b/src/utils/bridge.js @@ -5,6 +5,7 @@ import parseException from './exception-parser'; import { debug } from './logger'; import { isDevMode } from './dev-mode'; import { basicFetch } from './fetch'; +import { getBlockTypes } from '@wordpress/blocks'; /** * Generic function to dispatch messages to both Android and iOS bridges. @@ -91,8 +92,17 @@ export function onBlocksChanged( isEmpty = false ) { * * @return {void} */ -export function showBlockPicker() { - dispatchToBridge( 'showBlockPicker', {} ); +export function showBlockInserter() { + const blocks = getBlockTypes().map( ( blockType ) => { + return { + name: blockType.name, + title: blockType.title, + description: blockType.description, + category: blockType.category, + keywords: blockType.keywords || [], + }; + } ); + dispatchToBridge( 'showBlockInserter', { blocks } ); } /**