diff --git a/.gitignore b/.gitignore
index c43d6e0d4..1dc7d908a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
/phpcs.xml
/.phpcs.xml
*.zip
+/wp-includes/js/dist/*
/wp-includes/js/workbox*
/wiki
.vscode
diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist
index ded5795d2..35ecaf78b 100644
--- a/.phpcs.xml.dist
+++ b/.phpcs.xml.dist
@@ -13,6 +13,7 @@
./node_modules/
./vendor/
./build/
+ ./wp-includes/js/dist/
diff --git a/package.json b/package.json
index d4f99281f..fb70335f6 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,9 @@
"workbox-cli": "6.5.4"
},
"scripts": {
- "build": "grunt build; grunt create-build-zip",
+ "build": "npm run block:build; grunt build; grunt create-build-zip",
+ "block:build": "wp-scripts build site-icon-maskable=./wp-includes/js/src/site-icon-maskable/index.js --output-path=wp-includes/js/dist",
+ "block:start": "wp-scripts start site-icon-maskable=./wp-includes/js/src/site-icon-maskable/index.js --output-path=wp-includes/js/dist",
"check-engines": "wp-scripts check-engines",
"check-licenses": "wp-scripts check-licenses --production",
"deploy": "grunt deploy",
diff --git a/pwa.php b/pwa.php
index 5b909da62..6e2b6b81f 100644
--- a/pwa.php
+++ b/pwa.php
@@ -80,6 +80,7 @@ function _pwa_incorrect_plugin_slug_admin_notice() {
* Print admin notice when a build has not been been performed.
*
* @since 0.2
+ * @since 0.8.0-alpha Check for site-icon-maskable.asset.php, which should be auto-generated by wp-scripts
*/
function _pwa_print_build_needed_notice() {
?>
@@ -96,7 +97,10 @@ function _pwa_print_build_needed_notice() {
assertEquals( 10, has_action( 'wp_head', array( $this->instance, 'manifest_link_and_meta' ) ) );
$this->assertEquals( 10, has_action( 'rest_api_init', array( $this->instance, 'register_manifest_rest_route' ) ) );
- $this->assertEquals( 10, has_action( 'rest_api_init', array( $this->instance, 'register_short_name_setting' ) ) );
- $this->assertEquals( 10, has_action( 'admin_init', array( $this->instance, 'register_short_name_setting' ) ) );
+ $this->assertEquals( 10, has_action( 'rest_api_init', array( $this->instance, 'register_settings' ) ) );
+ $this->assertEquals( 10, has_action( 'admin_init', array( $this->instance, 'register_settings' ) ) );
$this->assertEquals( 10, has_action( 'admin_init', array( $this->instance, 'add_short_name_settings_field' ) ) );
}
@@ -532,16 +532,16 @@ public function test_get_url() {
}
/**
- * Test register_short_name_setting.
+ * Test register_settings.
*
- * @covers ::register_short_name_setting()
+ * @covers ::register_settings()
*/
- public function test_register_short_name_setting() {
+ public function test_register_settings() {
global $wp_registered_settings;
unset( $wp_registered_settings['short_name'] );
$this->assertArrayNotHasKey( 'short_name', $wp_registered_settings );
- $this->instance->register_short_name_setting();
+ $this->instance->register_settings();
$this->assertArrayHasKey( 'short_name', $wp_registered_settings );
$setting = $wp_registered_settings['short_name'];
@@ -549,6 +549,18 @@ public function test_register_short_name_setting() {
$this->assertEquals( 'general', $setting['group'] );
$this->assertEquals( array( $this->instance, 'sanitize_short_name' ), $setting['sanitize_callback'] );
$this->assertEquals( true, $setting['show_in_rest'] );
+
+ unset( $wp_registered_settings['site_icon_maskable'] );
+
+ $this->assertArrayNotHasKey( 'site_icon_maskable', $wp_registered_settings );
+ $this->instance->register_settings();
+ $this->assertArrayHasKey( 'site_icon_maskable', $wp_registered_settings );
+ $setting = $wp_registered_settings['site_icon_maskable'];
+
+ $this->assertEquals( 'boolean', $setting['type'] );
+ $this->assertEquals( 'general', $setting['group'] );
+ $this->assertEquals( 'rest_sanitize_boolean', $setting['sanitize_callback'] );
+ $this->assertEquals( true, $setting['show_in_rest'] );
}
/**
diff --git a/wp-includes/class-wp-web-app-manifest.php b/wp-includes/class-wp-web-app-manifest.php
index 5bdf32fa7..7b2b36126 100644
--- a/wp-includes/class-wp-web-app-manifest.php
+++ b/wp-includes/class-wp-web-app-manifest.php
@@ -68,9 +68,11 @@ public function init() {
add_action( 'rest_api_init', array( $this, 'register_manifest_rest_route' ) );
add_filter( 'site_status_tests', array( $this, 'add_pwa_site_health_tests' ) );
- add_action( 'rest_api_init', array( $this, 'register_short_name_setting' ) );
- add_action( 'admin_init', array( $this, 'register_short_name_setting' ) );
+ add_action( 'rest_api_init', array( $this, 'register_settings' ) );
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_init', array( $this, 'add_short_name_settings_field' ) );
+ add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_site_icon_maskable_block_editor_assets' ) );
+
}
/**
@@ -533,9 +535,15 @@ public static function get_url() {
}
/**
- * Register setting for short_name.
+ * Register pwa settings to the settings-API and make them visible to the REST API.
+ *
+ * @since 0.7.0
+ * @since 0.8.0-alpha Added registration of 'site_icon_maskable' setting.
+ *
+ * @return void
*/
- public function register_short_name_setting() {
+ public function register_settings() {
+ // Register setting for short_name.
register_setting(
'general',
self::SHORT_NAME_OPTION,
@@ -546,6 +554,29 @@ public function register_short_name_setting() {
'show_in_rest' => true,
)
);
+
+ /*
+ * Register setting for maskable site-icon.
+ *
+ * Even that this option is not exposed
+ * to the settings UI within normal admin options pages,
+ * the registration is needed to make the option
+ * available to the REST API, which is used by
+ * the site-editor.
+ *
+ * In the site-editor, this option can be set with the help of
+ * UI that is added to the 'site-logo' core block.
+ */
+ register_setting(
+ 'general',
+ 'site_icon_maskable',
+ array(
+ 'type' => 'boolean',
+ 'description' => __( 'Wether the current site icon is maskable or not, as this is needed by some devices.', 'pwa' ),
+ 'sanitize_callback' => 'rest_sanitize_boolean',
+ 'show_in_rest' => true,
+ )
+ );
}
/**
@@ -677,4 +708,37 @@ function updateShortNameField() {
{
+ return (props) => {
+ if (props.name !== 'core/site-logo') {
+ return ;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ };
+}, 'withMaskableControls');
+
+/**
+ * To modify the behavior of existing blocks,
+ * WordPress exposes several APIs.
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/
+ */
+wp.hooks.addFilter(
+ 'editor.BlockEdit',
+ 'pwa/with-maskable-icon-controls',
+ withMaskableControls
+);
diff --git a/wp-includes/js/src/site-icon-maskable/maskable-icon-controls.js b/wp-includes/js/src/site-icon-maskable/maskable-icon-controls.js
new file mode 100644
index 000000000..fdfa358c6
--- /dev/null
+++ b/wp-includes/js/src/site-icon-maskable/maskable-icon-controls.js
@@ -0,0 +1,115 @@
+/**
+ * Retrieves the translation of text.
+ *
+ * @see https://developer.wordpress.org/block-editor/packages/packages-i18n/
+ */
+import { __ } from '@wordpress/i18n';
+
+/**
+ * This module allows you to create and use standalone block editors. ;)
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-block-editor/
+ */
+import { InspectorControls } from '@wordpress/block-editor';
+
+/**
+ * This package includes a library of generic WordPress components
+ * to be used for creating common UI elements shared between screens
+ * and features of the WordPress dashboard.
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-components/
+ */
+import {
+ Flex,
+ FlexBlock,
+ FlexItem,
+ PanelBody,
+ ToggleControl,
+} from '@wordpress/components';
+
+/**
+ * Core Data is a data module intended to
+ * simplify access to and manipulation
+ * of core WordPress entities.
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-core-data/
+ */
+import { store as coreStore, useEntityProp } from '@wordpress/core-data';
+
+/**
+ * WordPress’ data module serves as a hub
+ * to manage application state
+ * for both plugins and WordPress itself.
+ *
+ * @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/
+ */
+import { useSelect } from '@wordpress/data';
+
+export default function MaskableControls() {
+ const [siteIconMaskable, setSiteIconMaskable] = useEntityProp(
+ 'root',
+ 'site',
+ 'site_icon_maskable'
+ );
+
+ // mainly borrowed from ...
+ const { isRequestingSiteIcon, siteIconUrl } = useSelect((select) => {
+ const { getEntityRecord, isResolving } = select(coreStore);
+ const siteData =
+ getEntityRecord('root', '__unstableBase', undefined) || {};
+
+ return {
+ isRequestingSiteIcon: isResolving('getEntityRecord', [
+ 'root',
+ '__unstableBase',
+ undefined,
+ ]),
+ siteIconUrl: siteData.site_icon_url,
+ };
+ }, []);
+
+ if (isRequestingSiteIcon) {
+ return null;
+ }
+
+ const siteIconStyle = {
+ clipPath: siteIconMaskable ? 'inset(10% round 50%)' : '',
+ width: '64px',
+ };
+
+ let siteIcon =
;
+
+ if (siteIconUrl) {
+ siteIcon = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ {siteIcon}
+
+
+
+ );
+}