diff --git a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/block.json b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/block.json
index b7a740c9..dd8d04b8 100644
--- a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/block.json
+++ b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/block.json
@@ -1,6 +1,6 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
- "apiVersion": 2,
+ "apiVersion": 3,
"name": "wporg/site-screenshot",
"version": "0.1.0",
"title": "Site Screenshot",
@@ -54,5 +54,7 @@
"usesContext": [ "postId" ],
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
- "style": "file:./style-index.css"
+ "viewScript": "file:./view.js",
+ "style": "file:./style-index.css",
+ "render": "file:../../src/site-screenshot/render.php"
}
diff --git a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/index.php b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/index.php
index 8e926924..179b397e 100644
--- a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/index.php
+++ b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/index.php
@@ -22,57 +22,7 @@
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function init() {
- register_block_type(
- dirname( dirname( __DIR__ ) ) . '/build/site-screenshot',
- array(
- 'render_callback' => __NAMESPACE__ . '\render',
- )
- );
-}
-
-/**
- * Render the block content.
- *
- * @param array $attributes Block attributes.
- * @param string $content Block default content.
- * @param WP_Block $block Block instance.
- *
- * @return string Returns the block markup.
- */
-function render( $attributes, $content, $block ) {
- if ( ! isset( $block->context['postId'] ) ) {
- return '';
- }
-
- $post_ID = $block->context['postId'];
- $post = get_post( $post_ID );
-
- $screenshot = get_site_screenshot_src( $post, $attributes['type'] );
-
- $loading = 'eager';
- if ( isset( $attributes['lazyLoad'] ) && true === $attributes['lazyLoad'] ) {
- $loading = 'lazy';
- }
-
- $img_content = sprintf(
- '',
- esc_url( $screenshot ),
- the_title_attribute( array( 'echo' => false ) ),
- esc_attr( $loading )
- );
-
- $classname = 'is-size-' . esc_attr( $attributes['type'] );
- if ( isset( $attributes['isLink'] ) && true == $attributes['isLink'] ) {
- $img_content = '' . $img_content . '';
- $classname .= ' is-linked-image';
- }
-
- $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classname ) );
- return sprintf(
- '
%s
',
- $wrapper_attributes,
- $img_content
- );
+ register_block_type( dirname( dirname( __DIR__ ) ) . '/build/site-screenshot' );
}
/**
@@ -109,7 +59,7 @@ function get_site_screenshot_src( $post, $type = 'desktop' ) {
'vpw' => 'mobile' === $type ? 375 : 1920,
'vph' => 'mobile' === $type ? 667 : 1080,
),
- 'https://wordpress.com/mshots/v1/' . urlencode( $requested_url ),
+ 'https://s0.wp.com/mshots/v1/' . urlencode( $requested_url ),
);
} elseif ( function_exists( 'jetpack_photon_url' ) ) {
// Use Jetpack cache for non mShot images
diff --git a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/render.php b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/render.php
new file mode 100644
index 00000000..9a6fc965
--- /dev/null
+++ b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/render.php
@@ -0,0 +1,67 @@
+context['postId'] ) ) {
+ return '';
+}
+
+$current_post = get_post( $block->context['postId'] );
+$has_link = isset( $attributes['isLink'] ) && true == $attributes['isLink'];
+$is_lazyload = isset( $attributes['lazyLoad'] ) && true === $attributes['lazyLoad'];
+
+$screenshot = get_site_screenshot_src( $current_post, $attributes['type'] );
+$is_mshots = str_contains( $screenshot, 'mshots' );
+
+$classname = 'is-size-' . esc_attr( $attributes['type'] );
+if ( $has_link ) {
+ $classname .= ' is-linked-image';
+}
+
+// Initial state to pass to Interactivity API.
+// This handles the image data (used to load image from mshots) and current
+// state information (like errors).
+$init_state = [
+ 'isMShots' => $is_mshots,
+ 'isLazyLoad' => $is_lazyload,
+ 'attempts' => 0,
+ 'base64Image' => '',
+ 'hasError' => false,
+ 'src' => esc_url( $screenshot ),
+ 'alt' => the_title_attribute( array( 'echo' => false ) ),
+];
+$encoded_state = wp_json_encode( [ 'wporg' => [ 'showcase' => [ 'screenshot' => $init_state ] ] ] );
+
+?>
+ $classname ) ); // phpcs:ignore ?>
+ data-wp-interactive
+ data-wp-context=""
+>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/style.scss b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/style.scss
index b27c846c..73003c9a 100644
--- a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/style.scss
+++ b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/style.scss
@@ -36,6 +36,14 @@
&:first-child {
flex: 4;
}
+
+ &.is-size-desktop .wporg-site-screenshot__loader {
+ --wporg-site-screenshot--loader--size: 100px;
+ }
+
+ &.is-size-mobile .wporg-site-screenshot__loader {
+ --wporg-site-screenshot--loader--size: 40px;
+ }
}
.is-section-site-hero & {
@@ -45,6 +53,7 @@
a {
display: block;
+ height: 100%;
&:focus,
&:focus-visible {
@@ -57,13 +66,61 @@
vertical-align: middle;
}
- &.is-size-desktop img {
+ &:not(.has-loaded).is-size-desktop {
aspect-ratio: 535 / 300;
}
- &.is-size-mobile img {
+ &:not(.has-loaded).is-size-mobile {
aspect-ratio: 375 / 600;
}
+
+ .wporg-site-screenshot__mshot-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+ }
+
+ .wporg-site-screenshot__loader {
+ --wporg-site-screenshot--loader--size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+
+ &::after {
+ content: "";
+ display: inline-block;
+ box-sizing: border-box;
+ height: var(--wporg-site-screenshot--loader--size);
+ width: var(--wporg-site-screenshot--loader--size);
+ border: calc(var(--wporg-site-screenshot--loader--size) * 0.1) solid;
+ border-color:
+ var(--wp--custom--wporg-site-screenshot--border--color)
+ var(--wp--custom--wporg-site-screenshot--border--color)
+ var(--wp--custom--link--color--text);
+ border-radius: 50%;
+ animation: rotate-360 1.4s linear infinite;
+ }
+ }
+
+ .wporg-site-screenshot__error {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ }
+}
+
+@keyframes rotate-360 {
+ 0% {
+ transform: rotate(0deg);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
}
.wp-block-group.has-feature-color-background > .wp-block-group {
diff --git a/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/view.js b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/view.js
new file mode 100644
index 00000000..76b5d547
--- /dev/null
+++ b/source/wp-content/themes/wporg-showcase-2022/src/site-screenshot/view.js
@@ -0,0 +1,143 @@
+/* global FileReader */
+/**
+ * WordPress dependencies
+ */
+import { store as wpStore } from '@wordpress/interactivity';
+
+/**
+ * Module constants
+ */
+const MAX_ATTEMPTS = 10;
+const RETRY_DELAY = 2000;
+
+/**
+ * Helper to update the "attempts" value.
+ *
+ * @param {Object} store
+ */
+function increaseAttempts( store ) {
+ store.context.wporg.showcase.screenshot.attempts++;
+}
+
+/**
+ * Helper to update the "shouldRetry" value.
+ *
+ * @param {boolean} value
+ * @param {Object} store
+ */
+function setShouldRetry( value, store ) {
+ store.context.wporg.showcase.screenshot.shouldRetry = value;
+}
+
+/**
+ * Helper to update the "hasError" value.
+ *
+ * @param {boolean} value
+ * @param {Object} store
+ */
+function setHasError( value, store ) {
+ store.context.wporg.showcase.screenshot.hasError = value;
+}
+
+/**
+ * Helper to update the "base64Image" value.
+ *
+ * @param {string} value
+ * @param {Object} store
+ */
+function setBase64Image( value, store ) {
+ store.context.wporg.showcase.screenshot.base64Image = value;
+}
+
+/**
+ * Make a request to the mShots URL, update state values.
+ *
+ * @param {string} fullUrl
+ * @param {Object} store
+ */
+const fetchImage = async ( fullUrl, store ) => {
+ try {
+ const res = await fetch( fullUrl );
+ increaseAttempts( store );
+
+ if ( res.redirected ) {
+ setShouldRetry( true, store );
+ } else if ( res.status === 200 && ! res.redirected ) {
+ const blob = await res.blob();
+
+ const reader = new FileReader();
+ reader.onload = ( event ) => {
+ setBase64Image( event.target.result, store );
+ };
+ reader.readAsDataURL( blob );
+
+ setShouldRetry( false, store );
+ }
+ } catch ( error ) {
+ setHasError( true, store );
+ setShouldRetry( false, store );
+ }
+};
+
+wpStore( {
+ effects: {
+ wporg: {
+ showcase: {
+ screenshot: {
+ // Run on init, starts the image fetch process.
+ init: async ( store ) => {
+ const { context } = store;
+ const { base64Image, isMShots, src } = context.wporg.showcase.screenshot;
+
+ if ( isMShots && ! base64Image ) {
+ // Initial fetch.
+ await fetchImage( src, store );
+
+ // Set up the function to retry every RETRY_DELAY (2 seconds).
+ const intervalId = setInterval(
+ async ( _context ) => {
+ const { attempts, base64Image: _base64Image, shouldRetry } = _context;
+ if ( shouldRetry ) {
+ await fetchImage( src, store );
+ }
+ if ( attempts >= MAX_ATTEMPTS ) {
+ clearInterval( intervalId );
+ if ( ! _base64Image ) {
+ setHasError( true, store );
+ }
+ }
+ },
+ RETRY_DELAY,
+ context.wporg.showcase.screenshot
+ );
+ }
+ },
+
+ // Run as an effect, when the context changes.
+ update: ( store ) => {
+ const { context, ref } = store;
+ const { alt, base64Image, hasError, isMShots } = context.wporg.showcase.screenshot;
+
+ if ( ! isMShots ) {
+ return;
+ }
+
+ if ( hasError ) {
+ const spinner = ref.querySelector( 'div' );
+ spinner.classList.remove( 'wporg-site-screenshot__loader' );
+ spinner.classList.add( 'wporg-site-screenshot__error' );
+ spinner.innerText = alt;
+ ref.parentElement.classList.remove( 'has-loaded' );
+ } else if ( base64Image ) {
+ const img = document.createElement( 'img' );
+ img.src = base64Image;
+ img.alt = alt;
+ ref.replaceChildren( img );
+ ref.parentElement.classList.add( 'has-loaded' );
+ }
+ },
+ },
+ },
+ },
+ },
+} );