GoogleForCreators / web-stories-wp

Web Stories for WordPress
https://wp.stories.google
Apache License 2.0
758 stars 179 forks source link

Use Interactivity API for frontend functionality #13704

Open Swanand01 opened 1 month ago

Swanand01 commented 1 month ago

Summary

This PR makes use of the new Interactivity API to implement the lightbox functionality in the Web Stories block.

On WP versions < 6.5

On WP versions > 6.5

To learn more about these, please visit General overview of the Interactivity API

User-facing changes

None. The user won't notice a difference in how the Stories are rendered and opened.

Testing Instructions

This PR can be tested by following these steps:

  1. Ensure that you have WordPress version >= 6.5
  2. Add the Web Stories block to a page.
  3. View the page and examine the markup of the block.
  4. You should see directives (attributes) like data-wp-interactive

Reviews

Does this PR have a security-related impact?

No.

Does this PR change what data or activity we track or use?

No.

Does this PR have a legal-related impact?

No.

Checklist


Fixes #13502

Swanand01 commented 1 month ago

I've tried to replicate the following functionality using the Interactivity API:

  1. Opening the story lightbox
  2. Closing the story lightbox
  3. Navigation between stories.

Using the Interactivity API requires adding a viewScript in the block.json file, so I have placed the view.js file in the same folder as the block.json file.

This file mirrors the web-stories-lightbox.js file, which was used to perform the above-mentioned functionality. The main difference here, is that we call actions directly, instead of using event listeners.

These actions are defined in view.js, and are called from the DOM elements in Renderer.php.

Swanand01 commented 1 month ago

https://github.com/GoogleForCreators/web-stories-wp/blob/8054547eb5834cdc12e1b5d127d141aa8d709162/includes/Renderer/Stories/Renderer.php#L439-L446

I think we'll need to add these attributes to the <amp-story-player /> in Embed.php and Singleton.php as well.

WDYT @swissspidy ?

swissspidy commented 4 weeks ago

It took me a moment to realize that the interactivity API somehow doesn't work with classic themes... I'm probably missing something, but I opened https://core.trac.wordpress.org/ticket/61387 just in case. But if it's truly a bug, then this is a blocker for us.

Implementation-wise it looks like a nice port of the existing script 👍 It's also super snappy.

Functionality-wise, later on we could try to convert the carousel script to this new format as well. I just saw a similar solution for a slider here: https://github.com/ryanwelcher/iapi-gallery-slider/blob/38b63eacf018f29153980fce7d7278d7b41f1011/src/view.js

So a carousel like ours could potentially also be built. But that's future talk :)

What's your experience so far with using this interactivity API?

Note: For the file location we'll have to come up with something different eventually as I don't want to put JS files into the blocks folder. Maybe this could be a new stories-block-view package under packages/ and then we copy some files with webpack if necessary. Then we could try to use TypeScript as well.

I think we'll need to add these attributes to the <amp-story-player /> in Embed.php and Singleton.php as well.

For Singleton, yes, as it is used to display a single story's poster image and then when you click on it, the player opens in a lightbox.

For Embed, no, as that one directly displays the player for a given story and does not use a lightbox at all.

In the editor you can switch between the two modes using this toggle:

Screenshot 2024-06-06 at 11 25 02

Another thing we need to be careful: we don't need to add this to the AMP version of the markup (everything wrapped in $this->context->is_amp()), as AMP handles the interactivity for us there (i.e. if one is using the AMP plugin)

swissspidy commented 4 weeks ago

It took me a moment to realize that the interactivity API somehow doesn't work with classic themes... I'm probably missing something, but I opened core.trac.wordpress.org/ticket/61387 just in case. But if it's truly a bug, then this is a blocker for us.

OK it's my fault for not reading the docs right.

So we'll definitely need to move this script to packages/stories-block-view and then add a corresponding entry to the webpack.config.cjs. This way, there will be an .asset.php file generated for the script.

For classic themes in particular we'll also need wp_interactivity_process_directives for server-side processing of these directives

Swanand01 commented 4 weeks ago

What's your experience so far with using this interactivity API?

I think it is a nice addition to have, and makes things simpler as I don't have to add event listeners manually. I also like the declarative approach. However, I also feel like it's not very intuitive, one can't really figure out what's going on by just looking at element attribute.

Swanand01 commented 3 weeks ago

Hi @swissspidy, I have created a new package stories-block-view, and added an entry in the webpack.config.js file as well.

For classic themes in particular we'll also need wp_interactivity_process_directives for server-side processing of these directives

How and where should this be implemented in our Renderer? Do I check if the theme is a classic theme, and then pass the stringified markup through wp_interactivity_process_directives, and then render that instead?

swissspidy commented 3 weeks ago

How and where should this be implemented in our Renderer? Do I check if the theme is a classic theme, and then pass the stringified markup through wp_interactivity_process_directives, and then render that instead?

I think it would need to be added at the end of the Carousel_Renderer::render() and Generic_Renderer::render() methods, before the output is running through a filter. Probably don't even need to check the theme, just call the function if it exists.

github-actions[bot] commented 3 weeks ago

Size Change: +8.97 kB (+0.33%)

Total Size: 2.77 MB

Filename Size Change
assets/js/web-stories-block-view.js 8.91 kB +8.91 kB (new file) 🆕
ℹ️ View Unchanged | Filename | Size | Change | | :--- | :---: | :---: | | `assets/css/web-stories-block-rtl.css` | 4.65 kB | 0 B | | `assets/css/web-stories-block.css` | 4.69 kB | 0 B | | `assets/css/web-stories-carousel-rtl.css` | 711 B | 0 B | | `assets/css/web-stories-carousel.css` | 711 B | 0 B | | `assets/css/web-stories-dashboard-rtl.css` | 657 B | 0 B | | `assets/css/web-stories-dashboard.css` | 659 B | 0 B | | `assets/css/web-stories-editor-rtl.css` | 767 B | 0 B | | `assets/css/web-stories-editor.css` | 769 B | 0 B | | `assets/css/web-stories-embed-rtl.css` | 662 B | 0 B | | `assets/css/web-stories-embed.css` | 664 B | 0 B | | `assets/css/web-stories-list-styles-rtl.css` | 2.43 kB | 0 B | | `assets/css/web-stories-list-styles.css` | 2.46 kB | 0 B | | `assets/css/web-stories-theme-style-twentyeleven-rtl.css` | 102 B | 0 B | | `assets/css/web-stories-theme-style-twentyeleven.css` | 102 B | 0 B | | `assets/css/web-stories-theme-style-twentyfifteen-rtl.css` | 253 B | 0 B | | `assets/css/web-stories-theme-style-twentyfifteen.css` | 253 B | 0 B | | `assets/css/web-stories-theme-style-twentyfourteen-rtl.css` | 286 B | 0 B | | `assets/css/web-stories-theme-style-twentyfourteen.css` | 286 B | 0 B | | `assets/css/web-stories-theme-style-twentyseventeen-rtl.css` | 310 B | 0 B | | `assets/css/web-stories-theme-style-twentyseventeen.css` | 310 B | 0 B | | `assets/css/web-stories-theme-style-twentysixteen-rtl.css` | 241 B | 0 B | | `assets/css/web-stories-theme-style-twentysixteen.css` | 241 B | 0 B | | `assets/css/web-stories-theme-style-twentyten-rtl.css` | 142 B | 0 B | | `assets/css/web-stories-theme-style-twentyten.css` | 142 B | 0 B | | `assets/css/web-stories-theme-style-twentytwelve-rtl.css` | 265 B | 0 B | | `assets/css/web-stories-theme-style-twentytwelve.css` | 265 B | 0 B | | `assets/css/web-stories-theme-style-twentytwenty-rtl.css` | 86 B | 0 B | | `assets/css/web-stories-theme-style-twentytwenty.css` | 86 B | 0 B | | `assets/css/web-stories-theme-style-twentytwentyone-rtl.css` | 324 B | 0 B | | `assets/css/web-stories-theme-style-twentytwentyone.css` | 324 B | 0 B | | `assets/css/web-stories-widget-rtl.css` | 459 B | 0 B | | `assets/css/web-stories-widget.css` | 459 B | 0 B | | `assets/js/3768.js` | 13.9 kB | 0 B | | `assets/js/3933.js` | 27.3 kB | 0 B | | `assets/js/4032.js` | 4.74 kB | 0 B | | `assets/js/5380.js` | 8.18 kB | 0 B | | `assets/js/7830.js` | 38.2 kB | 0 B | | `assets/js/911.js` | 218 kB | 0 B | | `assets/js/9391.js` | 95 B | 0 B | | `assets/js/945.js` | 49.5 kB | 0 B | | `assets/js/9947.js` | 97.6 kB | 0 B | | `assets/js/chunk-colorthief.js` | 2.63 kB | 0 B | | `assets/js/chunk-ffmpeg.js` | 5.98 kB | 0 B | | `assets/js/chunk-html-to-image.js` | 4.51 kB | 0 B | | `assets/js/chunk-media-gallery.js` | 6.12 kB | 0 B | | `assets/js/chunk-mediainfo.js` | 95 B | 0 B | | `assets/js/chunk-opentype.js` | 97 B | 0 B | | `assets/js/chunk-react-calendar.js` | 10.9 kB | 0 B | | `assets/js/chunk-react-color.js` | 25.9 kB | 0 B | | `assets/js/chunk-selfie-segmentation.js` | 16.3 kB | 0 B | | `assets/js/chunk-web-stories-template-0-metaData.js` | 546 B | 0 B | | `assets/js/chunk-web-stories-template-0.js` | 10.8 kB | 0 B | | `assets/js/chunk-web-stories-template-1-metaData.js` | 537 B | 0 B | | `assets/js/chunk-web-stories-template-1.js` | 9.16 kB | 0 B | | `assets/js/chunk-web-stories-template-10-metaData.js` | 531 B | 0 B | | `assets/js/chunk-web-stories-template-10.js` | 7.18 kB | 0 B | | `assets/js/chunk-web-stories-template-11-metaData.js` | 536 B | 0 B | | `assets/js/chunk-web-stories-template-11.js` | 8.77 kB | 0 B | | `assets/js/chunk-web-stories-template-12-metaData.js` | 494 B | 0 B | | `assets/js/chunk-web-stories-template-12.js` | 8.55 kB | 0 B | | `assets/js/chunk-web-stories-template-13-metaData.js` | 521 B | 0 B | | `assets/js/chunk-web-stories-template-13.js` | 6.84 kB | 0 B | | `assets/js/chunk-web-stories-template-14-metaData.js` | 580 B | 0 B | | `assets/js/chunk-web-stories-template-14.js` | 7.2 kB | 0 B | | `assets/js/chunk-web-stories-template-15-metaData.js` | 541 B | 0 B | | `assets/js/chunk-web-stories-template-15.js` | 8.68 kB | 0 B | | `assets/js/chunk-web-stories-template-16-metaData.js` | 586 B | 0 B | | `assets/js/chunk-web-stories-template-16.js` | 10.4 kB | 0 B | | `assets/js/chunk-web-stories-template-17-metaData.js` | 542 B | 0 B | | `assets/js/chunk-web-stories-template-17.js` | 8.96 kB | 0 B | | `assets/js/chunk-web-stories-template-18-metaData.js` | 582 B | 0 B | | `assets/js/chunk-web-stories-template-18.js` | 9.19 kB | 0 B | | `assets/js/chunk-web-stories-template-19-metaData.js` | 501 B | 0 B | | `assets/js/chunk-web-stories-template-19.js` | 9.05 kB | 0 B | | `assets/js/chunk-web-stories-template-2-metaData.js` | 584 B | 0 B | | `assets/js/chunk-web-stories-template-2.js` | 8.93 kB | 0 B | | `assets/js/chunk-web-stories-template-20-metaData.js` | 548 B | 0 B | | `assets/js/chunk-web-stories-template-20.js` | 8.61 kB | 0 B | | `assets/js/chunk-web-stories-template-21-metaData.js` | 536 B | 0 B | | `assets/js/chunk-web-stories-template-21.js` | 9.36 kB | 0 B | | `assets/js/chunk-web-stories-template-22-metaData.js` | 523 B | 0 B | | `assets/js/chunk-web-stories-template-22.js` | 7.37 kB | 0 B | | `assets/js/chunk-web-stories-template-23-metaData.js` | 600 B | 0 B | | `assets/js/chunk-web-stories-template-23.js` | 6.9 kB | 0 B | | `assets/js/chunk-web-stories-template-24-metaData.js` | 516 B | 0 B | | `assets/js/chunk-web-stories-template-24.js` | 11.1 kB | 0 B | | `assets/js/chunk-web-stories-template-25-metaData.js` | 541 B | 0 B | | `assets/js/chunk-web-stories-template-25.js` | 6.74 kB | 0 B | | `assets/js/chunk-web-stories-template-26-metaData.js` | 598 B | 0 B | | `assets/js/chunk-web-stories-template-26.js` | 6.89 kB | 0 B | | `assets/js/chunk-web-stories-template-27-metaData.js` | 541 B | 0 B | | `assets/js/chunk-web-stories-template-27.js` | 7.6 kB | 0 B | | `assets/js/chunk-web-stories-template-28-metaData.js` | 532 B | 0 B | | `assets/js/chunk-web-stories-template-28.js` | 8.69 kB | 0 B | | `assets/js/chunk-web-stories-template-29-metaData.js` | 561 B | 0 B | | `assets/js/chunk-web-stories-template-29.js` | 8.84 kB | 0 B | | `assets/js/chunk-web-stories-template-3-metaData.js` | 533 B | 0 B | | `assets/js/chunk-web-stories-template-3.js` | 8.16 kB | 0 B | | `assets/js/chunk-web-stories-template-30-metaData.js` | 574 B | 0 B | | `assets/js/chunk-web-stories-template-30.js` | 7.35 kB | 0 B | | `assets/js/chunk-web-stories-template-31-metaData.js` | 503 B | 0 B | | `assets/js/chunk-web-stories-template-31.js` | 9.83 kB | 0 B | | `assets/js/chunk-web-stories-template-32-metaData.js` | 551 B | 0 B | | `assets/js/chunk-web-stories-template-32.js` | 12.1 kB | 0 B | | `assets/js/chunk-web-stories-template-33-metaData.js` | 491 B | 0 B | | `assets/js/chunk-web-stories-template-33.js` | 8.83 kB | 0 B | | `assets/js/chunk-web-stories-template-34-metaData.js` | 570 B | 0 B | | `assets/js/chunk-web-stories-template-34.js` | 7.35 kB | 0 B | | `assets/js/chunk-web-stories-template-35-metaData.js` | 565 B | 0 B | | `assets/js/chunk-web-stories-template-35.js` | 8.61 kB | 0 B | | `assets/js/chunk-web-stories-template-36-metaData.js` | 574 B | 0 B | | `assets/js/chunk-web-stories-template-36.js` | 11.9 kB | 0 B | | `assets/js/chunk-web-stories-template-37-metaData.js` | 528 B | 0 B | | `assets/js/chunk-web-stories-template-37.js` | 6.09 kB | 0 B | | `assets/js/chunk-web-stories-template-38-metaData.js` | 568 B | 0 B | | `assets/js/chunk-web-stories-template-38.js` | 7.55 kB | 0 B | | `assets/js/chunk-web-stories-template-39-metaData.js` | 586 B | 0 B | | `assets/js/chunk-web-stories-template-39.js` | 7.7 kB | 0 B | | `assets/js/chunk-web-stories-template-4-metaData.js` | 562 B | 0 B | | `assets/js/chunk-web-stories-template-4.js` | 11.6 kB | 0 B | | `assets/js/chunk-web-stories-template-40-metaData.js` | 557 B | 0 B | | `assets/js/chunk-web-stories-template-40.js` | 9.73 kB | 0 B | | `assets/js/chunk-web-stories-template-41-metaData.js` | 569 B | 0 B | | `assets/js/chunk-web-stories-template-41.js` | 7.39 kB | 0 B | | `assets/js/chunk-web-stories-template-42-metaData.js` | 521 B | 0 B | | `assets/js/chunk-web-stories-template-42.js` | 6.79 kB | 0 B | | `assets/js/chunk-web-stories-template-43-metaData.js` | 555 B | 0 B | | `assets/js/chunk-web-stories-template-43.js` | 8.41 kB | 0 B | | `assets/js/chunk-web-stories-template-44-metaData.js` | 579 B | 0 B | | `assets/js/chunk-web-stories-template-44.js` | 10.6 kB | 0 B | | `assets/js/chunk-web-stories-template-45-metaData.js` | 566 B | 0 B | | `assets/js/chunk-web-stories-template-45.js` | 7.09 kB | 0 B | | `assets/js/chunk-web-stories-template-46-metaData.js` | 532 B | 0 B | | `assets/js/chunk-web-stories-template-46.js` | 5.08 kB | 0 B | | `assets/js/chunk-web-stories-template-47-metaData.js` | 589 B | 0 B | | `assets/js/chunk-web-stories-template-47.js` | 8.84 kB | 0 B | | `assets/js/chunk-web-stories-template-48-metaData.js` | 554 B | 0 B | | `assets/js/chunk-web-stories-template-48.js` | 8.68 kB | 0 B | | `assets/js/chunk-web-stories-template-49-metaData.js` | 515 B | 0 B | | `assets/js/chunk-web-stories-template-49.js` | 8.5 kB | 0 B | | `assets/js/chunk-web-stories-template-5-metaData.js` | 557 B | 0 B | | `assets/js/chunk-web-stories-template-5.js` | 9.43 kB | 0 B | | `assets/js/chunk-web-stories-template-50-metaData.js` | 504 B | 0 B | | `assets/js/chunk-web-stories-template-50.js` | 8.83 kB | 0 B | | `assets/js/chunk-web-stories-template-51-metaData.js` | 527 B | 0 B | | `assets/js/chunk-web-stories-template-51.js` | 9.92 kB | 0 B | | `assets/js/chunk-web-stories-template-52-metaData.js` | 603 B | 0 B | | `assets/js/chunk-web-stories-template-52.js` | 9.89 kB | 0 B | | `assets/js/chunk-web-stories-template-53-metaData.js` | 553 B | 0 B | | `assets/js/chunk-web-stories-template-53.js` | 5.61 kB | 0 B | | `assets/js/chunk-web-stories-template-54-metaData.js` | 544 B | 0 B | | `assets/js/chunk-web-stories-template-54.js` | 7.4 kB | 0 B | | `assets/js/chunk-web-stories-template-55-metaData.js` | 574 B | 0 B | | `assets/js/chunk-web-stories-template-55.js` | 6.88 kB | 0 B | | `assets/js/chunk-web-stories-template-56-metaData.js` | 542 B | 0 B | | `assets/js/chunk-web-stories-template-56.js` | 9.45 kB | 0 B | | `assets/js/chunk-web-stories-template-57-metaData.js` | 527 B | 0 B | | `assets/js/chunk-web-stories-template-57.js` | 14.2 kB | 0 B | | `assets/js/chunk-web-stories-template-58-metaData.js` | 551 B | 0 B | | `assets/js/chunk-web-stories-template-58.js` | 5.4 kB | 0 B | | `assets/js/chunk-web-stories-template-59-metaData.js` | 588 B | 0 B | | `assets/js/chunk-web-stories-template-59.js` | 8.66 kB | 0 B | | `assets/js/chunk-web-stories-template-6-metaData.js` | 566 B | 0 B | | `assets/js/chunk-web-stories-template-6.js` | 6.87 kB | 0 B | | `assets/js/chunk-web-stories-template-60-metaData.js` | 513 B | 0 B | | `assets/js/chunk-web-stories-template-60.js` | 8.83 kB | 0 B | | `assets/js/chunk-web-stories-template-7-metaData.js` | 566 B | 0 B | | `assets/js/chunk-web-stories-template-7.js` | 7.08 kB | 0 B | | `assets/js/chunk-web-stories-template-8-metaData.js` | 566 B | 0 B | | `assets/js/chunk-web-stories-template-8.js` | 8.28 kB | 0 B | | `assets/js/chunk-web-stories-template-9-metaData.js` | 577 B | 0 B | | `assets/js/chunk-web-stories-template-9.js` | 8.17 kB | 0 B | | `assets/js/chunk-web-stories-templates.js` | 584 B | 0 B | | `assets/js/chunk-web-stories-textset-0.js` | 4.57 kB | 0 B | | `assets/js/chunk-web-stories-textset-1.js` | 5.57 kB | 0 B | | `assets/js/chunk-web-stories-textset-2.js` | 6.79 kB | 0 B | | `assets/js/chunk-web-stories-textset-3.js` | 12.6 kB | 0 B | | `assets/js/chunk-web-stories-textset-4.js` | 3.88 kB | 0 B | | `assets/js/chunk-web-stories-textset-5.js` | 5.24 kB | 0 B | | `assets/js/chunk-web-stories-textset-6.js` | 4.96 kB | 0 B | | `assets/js/chunk-web-stories-textset-7.js` | 8.77 kB | 0 B | | `assets/js/generateBlurhash.worker.worker.js` | 1.16 kB | 0 B | | `assets/js/web-stories-activation-notice.js` | 22.7 kB | 0 B | | `assets/js/web-stories-block.js` | 27.5 kB | +51 B (+0.19%) | | `assets/js/web-stories-carousel.js` | 9.87 kB | 0 B | | `assets/js/web-stories-dashboard.js` | 63.5 kB | 0 B | | `assets/js/web-stories-editor.js` | 1.45 MB | 0 B | | `assets/js/web-stories-embed.js` | 20 B | 0 B | | `assets/js/web-stories-lightbox.js` | 7.31 kB | 0 B | | `assets/js/web-stories-tinymce-button.js` | 9.78 kB | 0 B | | `assets/js/web-stories-widget.js` | 554 B | 0 B |

compressed-size-action

Swanand01 commented 3 weeks ago

Hi @swissspidy, the Build & deploy action seems to be failing:

ERROR in ./packages/stories-block-view/src/index.js 1:40-136
Module not found: Error: Attempted to use WordPress script in a module: /home/runner/work/web-stories-wp/web-stories-wp/node_modules/react-refresh/runtime.js, which is not supported yet.

ERROR in ./node_modules/@pmmmwh/react-refresh-webpack-plugin/lib/runtime/RefreshUtils.js 2:14-46
Module not found: Error: Attempted to use WordPress script in a module: react-refresh/runtime, which is not supported yet.
 @ ./packages/stories-block-view/src/index.js 117:37-60

ERROR in ./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js 4:23-55
Module not found: Error: Attempted to use WordPress script in a module: react-refresh/runtime, which is not supported yet.

Do I need to change something in the webpack.config.cjs file?

Particularly in :

const webStoriesBlockView = {
  ...sharedConfig,
  output: { ...sharedConfig.output, module: true },
  experiments: { outputModule: true },
  entry: {
    'web-stories-block-view': './packages/stories-block-view/src/index.js',
  },
  plugins: [
    ...sharedConfig.plugins.filter(
      (plugin) => !(plugin instanceof DependencyExtractionWebpackPlugin)
    ),
    // React Fast Refresh.
    !isProduction && new ReactRefreshWebpackPlugin(),
    new WebpackBar({
      name: 'Web Stories Block View',
      color: '#a9db14',
    }),
    new DependencyExtractionWebpackPlugin(),
  ].filter(Boolean),
};

This might be of help.

swissspidy commented 3 weeks ago

Yeah need to remove the ReactRefreshWebpackPlugin line as this is not a React script.

googleforcreators-bot commented 3 weeks ago

Plugin builds for 8f9577b680c883656891ea4afce28258d4c7fe69 are ready :bellhop_bell:!

Swanand01 commented 3 weeks ago

Hi @swissspidy, this test is failing for PHP > 8.3:

Google\Web_Stories\Tests\Integration\Media\Types::test_get_allowed_mime_types_multisite

--- Expected
+++ Actual
@@ @@
     2 => Array (...)
     3 => Array (
         0 => 'audio/mpeg'
-        1 => 'audio/wav'
-        2 => 'audio/ogg'
+        1 => 'audio/aac'
+        2 => 'audio/wav'
+        3 => 'audio/ogg'
     )
     4 => Array (...)
 )

Interestingly, this test passed when run locally. My PHP version is 8.3.0, with WP 6.5.4

Action link

swissspidy commented 3 weeks ago

It‘s not the PHP version. It‘s because it‘s testing against WordPress trunk. It will probably fix itself in trunk. If not, we can open an issue for it, as it is unrelated to this PR.

swissspidy commented 1 week ago

Revisiting this:

The Interactivity API approach looks appealing, but given the duplicate code required to still support WP < 6.5, let's put this aside until we raise our minimum version requirement. Then we should be able to remove some of that code, and rewrite it in TypeScript too.