Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UX] Stored Playgrounds (no more data loss), multiple Playgrounds, UI WebApp Redesign #1731

Merged
merged 128 commits into from
Sep 27, 2024

Conversation

adamziel
Copy link
Collaborator

@adamziel adamziel commented Sep 5, 2024

Description

Implements a large part of the website redesign:

CleanShot 2024-09-14 at 10 24 57@2x

High-level changes shipped in this PR:

  • Multiple Playgrounds. Every temporary Playground can be saved either in the browser storage (OPFS) or in a local directory (Chrome desktop only for now).
  • New Playground settings options: Name name, language, multisite
  • URL as the source of truth for the application state
  • State management via Redux

This work is a convergence of 18+ months of effort and discussions. The new UI opens relieves the users from juggling ephemeral Playgrounds and losing their work. It opens up space for long-lived site configurations and additional integrations. We could bring over all the PR previewers and demos right into the Playground app.

Here's just a few features unblocked by this PR:

Breaking changes

TODO

Remaining work

  • Note breaking changes in this PR
  • Write a release note for https://make.wordpress.org/playground/
  • Make sure GitHub integration is working. Looks like OAuth connection leads to 404.
  • Fix temp site "Edit Settings" functionality to actually edit settings (forking a temp site can come in a follow-up PR)
  • Fix style issue with overlapping site name label with narrow site info views
  • Fix style issue with bottom "Open Site" and "WP Admin" buttons missing for mobile viewports
  • Make sure there is a path for existing OPFS sites to continue to load
  • Adjust E2E tests.
  • Reflect OPFS write error in UI when saving temp site fails
  • Find a path forward for try-wordpress to continue working after this PR
  • Figure out why does the browser get so choppy during OPFS save. It looks as if there was a lot of synchronous work going on. Shouldn't all the effort be done by a worker a non-blocking way?
  • Test with Safari and Firefox. Might require a local production setup as FF won't work with the Playground dev server.
  • Fix Safari error: Unhandled Promise Rejection: UnknownError: Invalid platform file handle when saving a temporary Playground to OPFS.
  • Fix to allow deleting site that fails to boot. This is possible when saving a temp site fails partway through.
  • Fix this crash:
		/**
		 * @TODO: Fix OPFS site storage write timeout that happens alongside 2000
		 *        "Cannot read properties of undefined (reading 'apply')" errors here:
		 * I suspect the postMessage call we do to the safari worker causes it to
		 * respond with another message and these unexpected exchange throws off
		 * Comlink. We should make Comlink ignore those.
		 */
		// redirectTo(PlaygroundRoute.site(selectSiteBySlug(state, siteSlug)));
  • Test different scenarios manually, in particular those involving Blueprints passed via hash
  • Ensure we have all the aria, name="" etc. accessibility attributes we need, see AXE tools for Chrome.
  • Update developer documentation on the storage query arg (it's removed in this PR)
  • Go through all the TODOs added in this PR and decide whether to solve or punt them
  • Handle errors like "site not found in OPFS", "files missing from a local directory"
  • Disable any Local Filesystem UI in browsers that don't support them. Don't just hide them, though. Provide a help text to explain why are they disabled.
  • Reduce the naming confusion, e.g. updateSite in redux-store.ts vs updateSite in site-storage.ts. What would an unambiguous code pattern look like?
  • Find a reliable and intuitive way of updating these deeply nested redux state properties. Right now we do an ad-hoc recursive merge that's slightly different for sites and clients. Which patterns used in other apps would make it intuitive?
  • Have a single entrypoint for each logical action such as "Create a new site", "Update site", "Select site" etc. that will take care of updating the redux store, updating OPFS, and updating the URL. My ideal scenario is calling something like updateSite(slug, newConfig) in a React Component and being done without thinking "ughh I still need to update OPFS" or "I also have to adjust that .json file over there"
  • Fix all the tiny design imperfections, e.g. cut-off labels in the site settings form.

Follow up work

  • Mark all the related blocked issues as unblocked on the project board, e.g. Load requested language by URL #1703, [UX] Stored Playgrounds (no more data loss), multiple Playgrounds, UI WebApp Redesign #1731, and more – see the All Tasks view
  • Update Tutorial - WordPress Playground  Learn#1583 with info that the redesign is now in and we're good to record a video tutorial.
  • Recover from an invalid local directory handle #1746
  • Write a note in What's new for developers? (October 2024)
  • Document the new site saving flow in packages/docs/site/docs/main/about/build.md cc @juanmaguitar
  • Update all the screenshots in the documentation cc @juanmaguitar
  • When the site fails to load via .list(), still return that site's info but make note of the error. Not showing that site on a list could greatly confuse the user ("Hey, where did my site go?"). Let's be explicit about problems.
  • Introduce notifications system to provide feedback about outcomes of various user actions.
  • Add non-minified WordPress versions to the "New site" modal.
  • Fix console.js:288 TypeError: Cannot read properties of undefined (reading 'apply') at comlink.ts:314:51 at Array.reduce (<anonymous>) at callback (comlink.ts:314:29) – it seems to happen at trunk, too.
  • Attribute log messages to the site that triggered them.
  • Take note of any interactions that we find frustrating or confusing. We can perhaps adjust them in a follow-up PR, but let's make sure we notice and document them here.
  • Solidify the functional tooling for transforming between URL, runtimeConfiguration, Blueprint, and site settings form state for both OPFS sites and in-memory sites. Let's see if we can make it reusable in Playground CLI.
  • Speed up OPFS interactions, saving a site can take quite a while.
  • A mobile-friendly modal architecture that doesn't stack modals, allows dismissing, and understands some modals (e.g. fatal error report) might have priority over other modals (e.g. connect to GitHub). Discuss whether modals should be declared at the top level, like here, or contextual to where the "Show modal" button is rendered.
  • Discuss the need to support strong, masked passwords over a simple password that's just "password". (Solved by passwordless login)
  • Duplicate site feature implemented as "Export site + import site" with the new core-first PHP tools from StreamChain: An API for streams-processing data (e.g. HTTP → ZIP → XML → HTML) adamziel/wxr-normalize#1 and https://github.com/adamziel/site-transfer-protocol
  • Retain temporary sites between site changes. Don't just trash their iframe and state when the user switches to another site.

Closes #1719

cc @brandonpayton

@brandonpayton
Copy link
Member

I've been reading over this and hope to contribute later today. (But if you happen to finish it, @adamziel, that is fine and good too :)

},
reducers: {
setPlaygroundClient: (
setActiveSite: (state, action: PayloadAction<SiteInfo>) => {
state.activeSite = action.payload;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check whether the site is part of the current sites list?

@adamziel
Copy link
Collaborator Author

adamziel commented Sep 5, 2024

I've been reading over this and hope to contribute later today. (But if you happen to finish it, @adamziel, that is fine and good too :)

Yay! I'm wrapping up for now so feel free to take over

@@ -22,7 +23,8 @@ const SITE_METADATA_FILENAME = 'playground-site-metadata.json';
* NOTE: We are using different storage terms than our query API in order
* to be more explicit about storage medium in the site metadata format.
*/
export type SiteStorageType = 'temporary' | 'opfs' | 'local-fs';
export const SiteStorageTypes = ['opfs', 'local-fs', 'none'] as const;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I temporarily changed all the storage options naming to these three, but before this PR is merged I'd love to ensure the Query API keeps working with today's storage options names.

Copy link
Member

@brandonpayton brandonpayton Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great if we could block merging PRs that contain changes with some special TODO pattern like @BEFORE-MERGE, so we could leave inline notes for ourselves and be stopped from merging if we forget to address them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we could just do this with a CI test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love that!

With this particular change, though, we may not need to do that since the storage Query API parameter is gone. Developer documentation will need updating for sure.

packages/playground/website/src/lib/site-storage.ts Outdated Show resolved Hide resolved
packages/playground/website/src/lib/site-storage.ts Outdated Show resolved Hide resolved
packages/playground/website/src/lib/site-storage.ts Outdated Show resolved Hide resolved
wpVersion:
resolvedBlueprint.preferredVersions?.wp ||
LatestMinifiedWordPressVersion,
phpVersion: resolveVersion(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we allow site info to specify an unsupported PHP version and simply warn in the UI and tell the user how it will be handled by the runtime?

Over time, previous site configurations may have PHP versions that are no longer supported, and IMO, it would be better not to lose the information that a site was created for a now-unsupported PHP version. And if a site is now fataling due to back-compat breaks between the requested and actual PHP versions, it would give the user a chance to understand why.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point ❤️ Let's do that.

if (requestedSiteSlug !== 'create') {
const siteInfo = await getSiteInfoBySlug(requestedSiteSlug!);
console.log({ siteInfo });
dispatch(setActiveSite(siteInfo!));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the identified site does not exist?

One scenario:

  1. User bookmarks Playground link for specific site slug/ID
  2. User deletes site, perhaps unintentionally, or clears browser storage
  3. User visits bookmark

Copy link
Collaborator Author

@adamziel adamziel Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a notification system to tell the user what happened as we redirect to the first site that exists. There are WordPress components for that. It should be fine to delay that to another PR.

@brandonpayton
Copy link
Member

It looks like we have code so there should be footer buttons, so this should be fairly straightforward to fix...

Check out #1759, I've adjusted how the panel works there – perhaps it will solve the problem here.

Thanks, @adamziel. Will do :)

Btw, I also noticed some errors with importing from Zip and GitHub integration auth and will be looking at those also.

The import error is database related, and I'm not sure if it has to do with a conflicting WP version or something else. The GitHub issue is that the OAuth connection flow led me to a GH page with a 404 response.

@brandonpayton
Copy link
Member

And local directory storage is currently broken for Chrome because:

Uncaught (in promise) InvalidStateError: Failed to execute 'createSyncAccessHandle' on 'FileSystemFileHandle': Access Handles may only be created on temporary file systems

This is because I converted file writing to use createSyncAccessHandle exclusively because it was a single code path and tested as faster or as fast everywhere. I can revert that change.

@adamziel
Copy link
Collaborator Author

@brandonpayton would it be easy to keep using the sync handles? They're much faster in general.

@brandonpayton
Copy link
Member

@brandonpayton would it be easy to keep using the sync handles? They're much faster in general.

@adamziel, the problem was that we were using sync handles everywhere (or did you mean to suggest async handles where possible?). Writing to the local FS in Chrome requires using createWritable(), so I switched back to using that interface for browsers that support it.

But if you indeed mean to suggest we stick with the sync interface where possible, I can add an exception handler to fall back to createWritable() when createSyncAccessHandle() doesn't work and the former interface is available.

@brandonpayton
Copy link
Member

I found an timing issue with converting legacy OPFS sites in Chrome Canary. Sometimes deleting the old site directory after mounting the new one fails due to a write conflict, and I wonder if it has something to do with journaling FS events to the legacy dir.

@brandonpayton
Copy link
Member

And local directory storage is currently broken for Chrome because:

Uncaught (in promise) InvalidStateError: Failed to execute 'createSyncAccessHandle' on 'FileSystemFileHandle': Access Handles may only be created on temporary file systems

OK, this is fixed now by falling back to the createWritable() interface. And if we want to continue using createSyncAccessHandle as much as possible, I can make that change.

@brandonpayton
Copy link
Member

I added the few remaining items to the checklist:

  • Make sure GitHub integration is working. Looks like OAuth connection leads to 404.
  • Fix style issue with overlapping site name label with narrow site info views
  • Fix style issue with bottom "Open Site" and "WP Admin" buttons missing for mobile viewports

@brandonpayton
Copy link
Member

Fixed the overlapping name text.

From:

To:

@brandonpayton
Copy link
Member

It turns out the missing "Homepage" and "WP Admin" buttons in mobile layout were due to the Temporary site notice pushing them off the bottom of the page. I fixed that somewhat, but there are still page heights where they start being forced off the bottom of the screen. What I'd prefer in that case is for the center info panel to shrink vertically and become scrollable.

@brandonpayton
Copy link
Member

OK, I fixed the bottom site info buttons being hidden for some mobile screen dimensions.

Fix issue with Edit Settings for Temp site showing different random name

This is not a logic bug but is currently a UX bug. The truth is that update settings for a Temporary site actually creates a new temporary site. Yet the link people click to do this says "Edit Site Settings", and the submit button on the settings dialog says "Update" which implies updating rather than creating a new thing.

@brandonpayton
Copy link
Member

Fix issue with Edit Settings for Temp site showing different random name

This is not a logic bug but is currently a UX bug. The truth is that update settings for a Temporary site actually creates a new temporary site. Yet the link people click to do this says "Edit Site Settings", and the submit button on the settings dialog says "Update" which implies updating rather than creating a new thing.

For this PR, I think it makes more sense to have this button just edit the actual temp site settings, and if we want a way to create a fork of the current temp site, that should be via a separate button in a follow-up PR.

Updated the TODO to be:

  • Fix temp site "Edit Settings" functionality to actually edit settings (forking a temp site can come in a follow-up PR)

@bgrgicak
Copy link
Collaborator

bgrgicak commented Sep 27, 2024

Should the site manager be closed by default so users see a running site before anything else?

Yes, on small screens when the preview isn't available.

On mobile, you will first see the menu, click on the menu to see your settings, and click on the Homepage button to get to the actual site.

This isn't an issue when the preview is visible because you can see the site and the full view is just one click away.

@adamziel
Copy link
Collaborator Author

adamziel commented Sep 27, 2024

For this PR, I think it makes more sense to have this button just edit the actual temp site settings, and if we want a way to create a fork of the current temp site, that should be via a separate button in a follow-up PR.

We don't have the plumbing to, e.g., change the WordPress version. I'd just change the label on that button to "Create a similar Playground" or so for now.

@brandonpayton
Copy link
Member

The GH OAuth issue appears to be related to a testing detail on my end. And @adamziel fixed the temp site editing concern.

I think we are good to ship this.

@brandonpayton brandonpayton merged commit 39d0023 into trunk Sep 27, 2024
5 of 6 checks passed
@brandonpayton brandonpayton deleted the routing branch September 27, 2024 17:00
@adamziel
Copy link
Collaborator Author

adamziel commented Sep 27, 2024

Let's make sure we don't default to the sidebar view on mobile.

Edit: Done in a415c79

brandonpayton pushed a commit that referenced this pull request Sep 28, 2024
## Motivation for the change, related issues

#1731 introduced multiple Playground management. However, visiting a URL
containing a Blueprint sometimes led to a creation of multiple temporary
sites due to a React effect running twice. This PR prevents that error
by delaying the new site creation until the effect settles down. In
particular, we're waiting for the site list to either get loaded or
error out.

## Testing Instructions (or ideally a Blueprint)

Go to
http://localhost:5400/website-server/?plugin=classic-editor&blueprint-url=https%3A%2F%2Fwordpress.org%2Fplugins%2Fwp-json%2Fplugins%2Fv1%2Fplugin%2Fclassic-editor%2Fblueprint.json%3Frev%3D3158978%26lang%3Den_US
and confirm you only got a single temporary site
brandonpayton added a commit that referenced this pull request Sep 28, 2024
## Motivation for the change, related issues

Some Playwright tests were broken by last-minute changes to #1731.

Fixes #1818

## Implementation details

- Fix selection of "edit settings" and "save settings" buttons to select
"Create a similar Playground" and "Create" buttons instead.
- Change playground viewport selection to account for there being
multiple possible viewports now. We were using an ID, but now we're
using a CSS class.
- Some expect() calls were failing, at least locally, and that was fixed
by awaiting them. They need to be async.

## Testing Instructions (or ideally a Blueprint)

- CI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Problems when blogname is set to Chinese Site manager sidebar
3 participants