From af4fa762d33ba264c689bfb4a750b498c06e4e3e Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 18:23:38 -0500 Subject: [PATCH 01/22] Add function to derive site name from slug --- .../playground/website/src/lib/state/redux/slice-sites.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/playground/website/src/lib/state/redux/slice-sites.ts b/packages/playground/website/src/lib/state/redux/slice-sites.ts index 2f47417a95..e159050038 100644 --- a/packages/playground/website/src/lib/state/redux/slice-sites.ts +++ b/packages/playground/website/src/lib/state/redux/slice-sites.ts @@ -98,6 +98,12 @@ export const getSitesLoadingState = (state: { export function deriveSlugFromSiteName(name: string) { return name.toLowerCase().replaceAll(' ', '-'); } +export function deriveSiteNameFromSlug(slug: string) { + return slug + .replaceAll('-', ' ') + .replaceAll(/\b\w/g, (c) => c.toUpperCase()) + .replaceAll(/WordPress/gi, 'WordPress'); +} /** * Updates the site metadata in the OPFS and in the redux state. From 3910c15ae3e506a321818549063019f8f4db3dc9 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 18:25:23 -0500 Subject: [PATCH 02/22] Add modal to prompt user to save for site slugs without a persisted site --- .../website/src/components/layout/index.tsx | 72 ++++++++++--------- .../components/missing-site-modal/index.tsx | 62 ++++++++++++++++ 2 files changed, 102 insertions(+), 32 deletions(-) create mode 100644 packages/playground/website/src/components/missing-site-modal/index.tsx diff --git a/packages/playground/website/src/components/layout/index.tsx b/packages/playground/website/src/components/layout/index.tsx index 25a6ee3211..bdd9c23ef7 100644 --- a/packages/playground/website/src/components/layout/index.tsx +++ b/packages/playground/website/src/components/layout/index.tsx @@ -34,6 +34,7 @@ import { } from '../../lib/state/redux/slice-ui'; import { ImportFormModal } from '../import-form-modal'; import { PreviewPRModal } from '../../github/preview-pr'; +import { MissingSiteModal } from '../missing-site-modal'; acquireOAuthTokenIfNeeded(); @@ -46,7 +47,8 @@ export const modalSlugs = { GITHUB_EXPORT: 'github-export', PREVIEW_PR_WP: 'preview-pr-wordpress', PREVIEW_PR_GUTENBERG: 'preview-pr-gutenberg', -} + MISSING_SITE_PROMPT: 'missing-site-prompt', +}; const displayMode = getDisplayModeFromQuery(); function getDisplayModeFromQuery(): DisplayMode { @@ -186,39 +188,45 @@ function Modals(blueprint: Blueprint) { } else if (currentModal === modalSlugs.PREVIEW_PR_GUTENBERG) { return ; } else if (currentModal === modalSlugs.GITHUB_IMPORT) { - return { - setGithubExportValues({ - repoUrl: url, - prNumber: pr?.toString(), - toPathInRepo: path, - prAction: pr ? 'update' : 'create', + return ( + ; + urlInformation: { owner, repo, type, pr }, + }) => { + setGithubExportValues({ + repoUrl: url, + prNumber: pr?.toString(), + toPathInRepo: path, + prAction: pr ? 'update' : 'create', + contentType, + plugin: pluginOrThemeName, + theme: pluginOrThemeName, + }); + setGithubExportFiles(files); + }} + /> + ); } else if (currentModal === modalSlugs.GITHUB_EXPORT) { - return { - setGithubExportValues(formValues); - setGithubExportFiles(undefined); - }} - />; + return ( + { + setGithubExportValues(formValues); + setGithubExportFiles(undefined); + }} + /> + ); + } else if (currentModal === modalSlugs.MISSING_SITE_PROMPT) { + return ; } if (query.get('gh-ensure-auth') === 'yes') { diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx new file mode 100644 index 0000000000..0c65083231 --- /dev/null +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -0,0 +1,62 @@ +import { Button, Flex, FlexItem } from '@wordpress/components'; +import { Modal } from '../modal'; +import { SitePersistButton } from '../site-manager/site-persist-button'; +import { + useAppDispatch, + useAppSelector, + selectActiveSite, +} from '../../lib/state/redux/store'; +import { setActiveModal } from '../../lib/state/redux/slice-ui'; +import { usePlaygroundClient } from '../../lib/use-playground-client'; + +export function MissingSiteModal() { + const dispatch = useAppDispatch(); + const closeModal = () => dispatch(setActiveModal(null)); + + const activeSite = useAppSelector((state) => selectActiveSite(state)); + const playground = usePlaygroundClient(activeSite?.slug); + + if (!activeSite) { + return null; + } + if (activeSite.metadata.storage !== 'none') { + return null; + } + + // TODO: Improve language for this modal + return ( + +

What do you want to do?

+ + + + + + + + + + +
+ ); +} From 2c469686ca2d58d9437ffd262bd342b42ef4f2fc Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 18:37:17 -0500 Subject: [PATCH 03/22] Prompt user to consider saving when site with specified slug does not exist --- .../ensure-playground-site-is-selected.tsx | 94 +++++++++++++------ 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx index 9d8cc32963..16d18d5b77 100644 --- a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx +++ b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx @@ -6,6 +6,7 @@ import { siteListingLoaded, selectSiteBySlug, setTemporarySiteSpec, + deriveSiteNameFromSlug, } from '../../lib/state/redux/slice-sites'; import { selectActiveSite, @@ -17,6 +18,8 @@ import { redirectTo } from '../../lib/state/url/router'; import { logger } from '@php-wasm/logger'; import { Blueprint } from '@wp-playground/blueprints'; import { usePrevious } from '../../lib/hooks/use-previous'; +import { modalSlugs } from '../layout'; +import { setActiveModal } from '../../lib/state/redux/slice-ui'; /** * Ensures the redux store always has an activeSite value. @@ -41,6 +44,8 @@ export function EnsurePlaygroundSiteIsSelected({ const requestedSiteObject = useAppSelector((state) => selectSiteBySlug(state, requestedSiteSlug!) ); + const promptIfSiteMissing = + url.searchParams.get('if-site-slug-missing') === 'prompt'; const prevUrl = usePrevious(url); useEffect(() => { @@ -72,14 +77,31 @@ export function EnsurePlaygroundSiteIsSelected({ if (requestedSiteSlug) { // If the site does not exist, redirect to a new temporary site. if (!requestedSiteObject) { - // @TODO: Notification: 'The requested site was not found. Redirecting to a new temporary site.' - logger.log( - 'The requested site was not found. Redirecting to a new temporary site.' - ); - const currentUrl = new URL(window.location.href); - currentUrl.searchParams.delete('site-slug'); - redirectTo(currentUrl.toString()); - return; + if (promptIfSiteMissing) { + // @TODO: Notification: 'The requested site was not found. Redirecting to a new temporary site.' + logger.log( + 'The requested site was not found. Creating a new temporary site.' + ); + + await createNewTemporarySite( + dispatch, + requestedSiteSlug + ); + + dispatch( + setActiveModal(modalSlugs.MISSING_SITE_PROMPT) + ); + return; + } else { + // @TODO: Notification: 'The requested site was not found. Redirecting to a new temporary site.' + logger.log( + 'The requested site was not found. Redirecting to a new temporary site.' + ); + const currentUrl = new URL(window.location.href); + currentUrl.searchParams.delete('site-slug'); + redirectTo(currentUrl.toString()); + return; + } } dispatch(setActiveSite(requestedSiteSlug)); @@ -98,29 +120,7 @@ export function EnsurePlaygroundSiteIsSelected({ return; } - // If the site slug is missing, create a new temporary site. - // Lean on the Query API parameters and the Blueprint API to - // create the new site. - const newUrl = new URL(window.location.href); - let blueprint: Blueprint | undefined = undefined; - try { - blueprint = await resolveBlueprintFromURL(newUrl); - } catch (e) { - logger.error('Error resolving blueprint:', e); - } - // Create a new site otherwise - const newSiteInfo = await dispatch( - setTemporarySiteSpec({ - metadata: { - originalBlueprint: blueprint, - }, - originalUrlParams: { - searchParams: parseSearchParams(newUrl.searchParams), - hash: newUrl.hash, - }, - }) - ); - dispatch(setActiveSite(newSiteInfo.slug)); + await createNewTemporarySite(dispatch); } ensureSiteIsSelected(); @@ -138,3 +138,35 @@ function parseSearchParams(searchParams: URLSearchParams) { } return params; } + +async function createNewTemporarySite( + dispatch: ReturnType, + requestedSiteSlug?: string +) { + // If the site slug is missing, create a new temporary site. + // Lean on the Query API parameters and the Blueprint API to + // create the new site. + const newUrl = new URL(window.location.href); + let blueprint: Blueprint | undefined = undefined; + try { + blueprint = await resolveBlueprintFromURL(newUrl); + } catch (e) { + logger.error('Error resolving blueprint:', e); + } + // Create a new site otherwise + const newSiteInfo = await dispatch( + setTemporarySiteSpec({ + metadata: { + originalBlueprint: blueprint, + name: requestedSiteSlug + ? deriveSiteNameFromSlug(requestedSiteSlug) + : undefined, + }, + originalUrlParams: { + searchParams: parseSearchParams(newUrl.searchParams), + hash: newUrl.hash, + }, + }) + ); + await dispatch(setActiveSite(newSiteInfo.slug)); +} From bb554fa926628e438a92687e0239336fe75a8186 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 19:03:38 -0500 Subject: [PATCH 04/22] Multiple tweaks to prompt modal --- .../components/missing-site-modal/index.tsx | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index 0c65083231..0e62cdda18 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -8,13 +8,24 @@ import { } from '../../lib/state/redux/store'; import { setActiveModal } from '../../lib/state/redux/slice-ui'; import { usePlaygroundClient } from '../../lib/use-playground-client'; +import { useState } from 'react'; export function MissingSiteModal() { const dispatch = useAppDispatch(); const closeModal = () => dispatch(setActiveModal(null)); const activeSite = useAppSelector((state) => selectActiveSite(state)); - const playground = usePlaygroundClient(activeSite?.slug); + const playgroundClient = usePlaygroundClient(activeSite?.slug); + + const [playgroundReady, setPlaygroundReady] = useState< + boolean | Promise + >(false); + + if (playgroundClient && playgroundReady === false) { + const promiseToBeReady = playgroundClient.isReady(); + setPlaygroundReady(promiseToBeReady); + promiseToBeReady.then(() => setPlaygroundReady(true)); + } if (!activeSite) { return null; @@ -32,27 +43,28 @@ export function MissingSiteModal() { onRequestClose={closeModal} >

What do you want to do?

- + From bbb99d5b10c91f83050658f52995ae652a322f10 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 20:36:50 -0500 Subject: [PATCH 05/22] Fix wait until playground ready --- .../components/missing-site-modal/index.tsx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index 0e62cdda18..f2795d3a0c 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -8,7 +8,7 @@ import { } from '../../lib/state/redux/store'; import { setActiveModal } from '../../lib/state/redux/slice-ui'; import { usePlaygroundClient } from '../../lib/use-playground-client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; export function MissingSiteModal() { const dispatch = useAppDispatch(); @@ -17,16 +17,12 @@ export function MissingSiteModal() { const activeSite = useAppSelector((state) => selectActiveSite(state)); const playgroundClient = usePlaygroundClient(activeSite?.slug); - const [playgroundReady, setPlaygroundReady] = useState< - boolean | Promise - >(false); - - if (playgroundClient && playgroundReady === false) { - const promiseToBeReady = playgroundClient.isReady(); - setPlaygroundReady(promiseToBeReady); - promiseToBeReady.then(() => setPlaygroundReady(true)); - } - + const [playgroundReady, setPlaygroundReady] = useState(false); + useEffect(() => { + if (playgroundClient) { + playgroundClient.isReady().then(() => setPlaygroundReady(true)); + } + }, [playgroundClient]); if (!activeSite) { return null; } From 95e891d42501619b9db50811bb823328946db61c Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 21:16:38 -0500 Subject: [PATCH 06/22] Add TODO --- .../ensure-playground-site-is-selected.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx index 16d18d5b77..3acba76a7e 100644 --- a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx +++ b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx @@ -88,6 +88,7 @@ export function EnsurePlaygroundSiteIsSelected({ requestedSiteSlug ); + // TODO: Wait for Playground to load before showing the modal which would otherwise obscure the progress indicator dispatch( setActiveModal(modalSlugs.MISSING_SITE_PROMPT) ); From d70b1a2d7bcbcb51e21cc39ac4a0099cf06fc404 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 21:18:43 -0500 Subject: [PATCH 07/22] Improve modal text --- .../src/components/missing-site-modal/index.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index f2795d3a0c..a51f50302d 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -35,10 +35,16 @@ export function MissingSiteModal() { -

What do you want to do?

+

+ WordPress Playground tried to load a Playground that does not + exist, so we loaded a temporary Playground instead. Your changes + will be lost on page refresh. +

+

Would you like to save this Playground to browser storage?

From d12cffbd0a77e7b9e4bca1e01aaf318d860e1ef5 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 22:22:47 -0500 Subject: [PATCH 08/22] Adjust name of query param --- .../ensure-playground-site-is-selected.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx index 3acba76a7e..f115d413da 100644 --- a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx +++ b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx @@ -45,7 +45,7 @@ export function EnsurePlaygroundSiteIsSelected({ selectSiteBySlug(state, requestedSiteSlug!) ); const promptIfSiteMissing = - url.searchParams.get('if-site-slug-missing') === 'prompt'; + url.searchParams.get('if-stored-site-missing') === 'prompt'; const prevUrl = usePrevious(url); useEffect(() => { From 7eb79008ea6e9d66caf64ef79dd168f49b1c4751 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 22:23:14 -0500 Subject: [PATCH 09/22] Avoid modal being accidentally dismissed without making a choice --- .../website/src/components/missing-site-modal/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index a51f50302d..5c3f296f58 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -23,6 +23,7 @@ export function MissingSiteModal() { playgroundClient.isReady().then(() => setPlaygroundReady(true)); } }, [playgroundClient]); + if (!activeSite) { return null; } @@ -37,6 +38,8 @@ export function MissingSiteModal() { contentLabel="This is a dialog window which overlays the main content of the page. It offers the user a choice between using a temporary Playground and a persistent Playground that is saved to browser storage." + isDismissible={false} + shouldCloseOnClickOutside={false} onRequestClose={closeModal} >

From 0986c6b7bef1987bdfa7e2e2e58a23189578a689 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 23:34:42 -0500 Subject: [PATCH 10/22] Try to improve modal language --- .../website/src/components/missing-site-modal/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index 5c3f296f58..985fb394b8 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -44,8 +44,8 @@ export function MissingSiteModal() { >

WordPress Playground tried to load a Playground that does not - exist, so we loaded a temporary Playground instead. Your changes - will be lost on page refresh. + exist, so we loaded a temporary Playground instead. Any changes + to temporary Playgrounds are lost on page refresh.

Would you like to save this Playground to browser storage?

From b148c5643a6ce80d0d4c865e37a14655da11d44e Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 23:43:18 -0500 Subject: [PATCH 11/22] Display modal after Playground loads --- .../ensure-playground-site-is-selected.tsx | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx index f115d413da..b72a64b256 100644 --- a/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx +++ b/packages/playground/website/src/components/ensure-playground-site/ensure-playground-site-is-selected.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { resolveBlueprintFromURL } from '../../lib/state/url/resolve-blueprint-from-url'; import { useCurrentUrl } from '../../lib/state/url/router-hooks'; import { opfsSiteStorage } from '../../lib/state/opfs/opfs-site-storage'; @@ -20,6 +20,7 @@ import { Blueprint } from '@wp-playground/blueprints'; import { usePrevious } from '../../lib/hooks/use-previous'; import { modalSlugs } from '../layout'; import { setActiveModal } from '../../lib/state/redux/slice-ui'; +import { selectClientBySiteSlug } from '../../lib/state/redux/slice-clients'; /** * Ensures the redux store always has an activeSite value. @@ -44,6 +45,14 @@ export function EnsurePlaygroundSiteIsSelected({ const requestedSiteObject = useAppSelector((state) => selectSiteBySlug(state, requestedSiteSlug!) ); + const requestedClientInfo = useAppSelector( + (state) => + requestedSiteSlug && + selectClientBySiteSlug(state, requestedSiteSlug) + ); + const [needMissingSitePromptForSlug, setNeedMissingSitePromptForSlug] = + useState(false); + const promptIfSiteMissing = url.searchParams.get('if-stored-site-missing') === 'prompt'; const prevUrl = usePrevious(url); @@ -87,11 +96,7 @@ export function EnsurePlaygroundSiteIsSelected({ dispatch, requestedSiteSlug ); - - // TODO: Wait for Playground to load before showing the modal which would otherwise obscure the progress indicator - dispatch( - setActiveModal(modalSlugs.MISSING_SITE_PROMPT) - ); + setNeedMissingSitePromptForSlug(requestedSiteSlug); return; } else { // @TODO: Notification: 'The requested site was not found. Redirecting to a new temporary site.' @@ -128,6 +133,22 @@ export function EnsurePlaygroundSiteIsSelected({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [url.href, requestedSiteSlug, siteListingStatus]); + useEffect(() => { + if ( + needMissingSitePromptForSlug && + needMissingSitePromptForSlug === requestedSiteSlug && + requestedClientInfo + ) { + dispatch(setActiveModal(modalSlugs.MISSING_SITE_PROMPT)); + setNeedMissingSitePromptForSlug(false); + } + }, [ + needMissingSitePromptForSlug, + requestedSiteSlug, + requestedClientInfo, + dispatch, + ]); + return children; } From 3e03aa51663974199e460fee4d8f7706c819464b Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 4 Dec 2024 23:58:17 -0500 Subject: [PATCH 12/22] Disable use-temp button when saving to OPFS --- .../components/missing-site-modal/index.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index 985fb394b8..e1620b88df 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -7,22 +7,18 @@ import { selectActiveSite, } from '../../lib/state/redux/store'; import { setActiveModal } from '../../lib/state/redux/slice-ui'; -import { usePlaygroundClient } from '../../lib/use-playground-client'; -import { useEffect, useState } from 'react'; +import { selectClientInfoBySiteSlug } from '../../lib/state/redux/slice-clients'; export function MissingSiteModal() { const dispatch = useAppDispatch(); const closeModal = () => dispatch(setActiveModal(null)); const activeSite = useAppSelector((state) => selectActiveSite(state)); - const playgroundClient = usePlaygroundClient(activeSite?.slug); - - const [playgroundReady, setPlaygroundReady] = useState(false); - useEffect(() => { - if (playgroundClient) { - playgroundClient.isReady().then(() => setPlaygroundReady(true)); - } - }, [playgroundClient]); + const clientInfo = useAppSelector( + (state) => + activeSite?.slug && + selectClientInfoBySiteSlug(state, activeSite?.slug) + ); if (!activeSite) { return null; @@ -52,7 +48,10 @@ export function MissingSiteModal() { + + - -
); From 651c1d55841c418d8674c256d5ba19cd84307931 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 5 Dec 2024 11:48:24 -0500 Subject: [PATCH 17/22] Allow PersistSiteButton to save to single storage type --- .../site-persist-button/index.tsx | 24 +++++++++++++++++-- .../lib/state/redux/persist-temporary-site.ts | 3 ++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/packages/playground/website/src/components/site-manager/site-persist-button/index.tsx b/packages/playground/website/src/components/site-manager/site-persist-button/index.tsx index 611b27bede..865704ca3d 100644 --- a/packages/playground/website/src/components/site-manager/site-persist-button/index.tsx +++ b/packages/playground/website/src/components/site-manager/site-persist-button/index.tsx @@ -11,13 +11,16 @@ import { persistTemporarySite } from '../../../lib/state/redux/persist-temporary import { selectClientInfoBySiteSlug } from '../../../lib/state/redux/slice-clients'; import { useLocalFsAvailability } from '../../../lib/hooks/use-local-fs-availability'; import { isOpfsAvailable } from '../../../lib/state/opfs/opfs-site-storage'; +import { SiteStorageType } from '../../../lib/site-metadata'; export function SitePersistButton({ siteSlug, children, + storage = null, }: { siteSlug: string; children: React.ReactNode; + storage?: Extract | null; }) { const clientInfo = useAppSelector((state) => selectClientInfoBySiteSlug(state, siteSlug) @@ -26,8 +29,19 @@ export function SitePersistButton({ const dispatch = useAppDispatch(); if (!clientInfo?.opfsSync || clientInfo.opfsSync?.status === 'error') { - return ( - <> + let button = null; + if (storage) { + button = ( +
+ dispatch(persistTemporarySite(siteSlug, storage)) + } + > + {children} +
+ ); + } else { + button = ( + ); + } + + return ( + <> + {button} {clientInfo?.opfsSync?.status === 'error' && (
There has been an error. Please try again. diff --git a/packages/playground/website/src/lib/state/redux/persist-temporary-site.ts b/packages/playground/website/src/lib/state/redux/persist-temporary-site.ts index 26560346d4..ece5fa16d4 100644 --- a/packages/playground/website/src/lib/state/redux/persist-temporary-site.ts +++ b/packages/playground/website/src/lib/state/redux/persist-temporary-site.ts @@ -14,10 +14,11 @@ import { updateSiteMetadata, } from './slice-sites'; import { PlaygroundRoute, redirectTo } from '../url/router'; +import { SiteStorageType } from '../../site-metadata'; export function persistTemporarySite( siteSlug: string, - storageType: 'opfs' | 'local-fs' + storageType: Extract ) { // @TODO: Handle errors return async ( From 621ddd5ad54d9ca11678d7050dd93dc5f072fb62 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 5 Dec 2024 11:50:04 -0500 Subject: [PATCH 18/22] Only save to OPFS --- .../website/src/components/missing-site-modal/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index 417de1f97b..8e10a693fe 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -61,7 +61,10 @@ export function MissingSiteModal() { justify="flex-start" > - + From 04552852f7aec0defd2a29687162ae8cff8e06b0 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 5 Dec 2024 12:01:21 -0500 Subject: [PATCH 20/22] Increase space between buttons --- .../website/src/components/missing-site-modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index a4151157fd..f140e0c105 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -55,7 +55,7 @@ export function MissingSiteModal() { */} Date: Thu, 5 Dec 2024 12:02:27 -0500 Subject: [PATCH 21/22] Make Playground name visually distinguished from surrounding modal text --- .../website/src/components/missing-site-modal/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/playground/website/src/components/missing-site-modal/index.tsx b/packages/playground/website/src/components/missing-site-modal/index.tsx index f140e0c105..af7b57ff02 100644 --- a/packages/playground/website/src/components/missing-site-modal/index.tsx +++ b/packages/playground/website/src/components/missing-site-modal/index.tsx @@ -39,8 +39,8 @@ export function MissingSiteModal() { onRequestClose={closeModal} >

- The {activeSite.metadata.name} Playground does not exist, so we - loaded a temporary Playground instead. + The {activeSite.metadata.name} Playground does not exist, + so we loaded a temporary Playground instead.

If you want to preserve your changes, you can save the From fb168727328fcc4fe2037186ef112d728dd8c492 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Thu, 5 Dec 2024 12:13:51 -0500 Subject: [PATCH 22/22] Document if-stored-site-missing query param --- .../developers/06-apis/query-api/01-index.md | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/docs/site/docs/developers/06-apis/query-api/01-index.md b/packages/docs/site/docs/developers/06-apis/query-api/01-index.md index 911afa26fe..fd2170e0e3 100644 --- a/packages/docs/site/docs/developers/06-apis/query-api/01-index.md +++ b/packages/docs/site/docs/developers/06-apis/query-api/01-index.md @@ -21,25 +21,26 @@ You can go ahead and try it out. The Playground will automatically install the t ## Available options -| Option | Default Value | Description | -| --------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `php` | `8.0` | Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, or `latest`. | -| `wp` | `latest` | Loads the specified WordPress version. Accepts the last three major WordPress versions. As of June 1, 2024, that's `6.3`, `6.4`, or `6.5`. You can also use the generic values `latest`, `nightly`, or `beta`. | -| `blueprint-url` | | The URL of the Blueprint that will be used to configure this Playground instance. | -| `networking` | `no` | Enables or disables the networking support for Playground. Accepts `yes` or `no`. | -| `plugin` | | Installs the specified plugin. Use the plugin name from the WordPress Plugins Directory URL. For example, if the URL is `https://wordpress.org/plugins/wp-lazy-loading/`, the plugin name would be `wp-lazy-loading`. You can pre-install multiple plugins by saying `plugin=coblocks&plugin=wp-lazy-loading&…`. Installing a plugin automatically logs the user in as an admin. | -| `theme` | | Installs the specified theme. Use the theme name from the WordPress Themes Directory URL. For example, if the URL is `https://wordpress.org/themes/disco/`, the theme name would be `disco`. Installing a theme automatically logs the user in as an admin. | -| `url` | `/wp-admin/` | Load the specified initial WordPress page in this Playground instance. | -| `mode` | `browser-full-screen` | Determines how the WordPress instance is displayed. Either wrapped in a browser UI or full width as a seamless experience. Accepts `browser-full-screen`, or `seamless`. | -| `lazy` | | Defer loading the Playground assets until someone clicks on the "Run" button. Does not accept any values. If `lazy` is added as a URL parameter, loading will be deferred. | -| `login` | `yes` | Log the user in as an admin. Accepts `yes` or `no`. | -| `multisite` | `no` | Enables the WordPress multisite mode. Accepts `yes` or `no`. | -| `import-site` | | Imports site files and database from a ZIP file specified by a URL. | -| `import-wxr` | | Imports site content from a WXR file specified by a URL. It uses the WordPress Importer plugin, so the default admin user must be logged in. | -| `site-slug` | | Selects which site to load from browser storage. | -| `language` | `en_US` | Sets the locale for the WordPress instance. This must be used in combination with `networking=yes` otherwise WordPress won't be able to download translations. | -| `core-pr` | | Installs a specific https://github.com/WordPress/wordpress-develop core PR. Accepts the PR number. For example, `core-pr=6883`. | -| `gutenberg-pr` | | Installs a specific https://github.com/WordPress/gutenberg PR. Accepts the PR number. For example, `gutenberg-pr=65337`. | +| Option | Default Value | Description | +| ------------------------ | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `php` | `8.0` | Loads the specified PHP version. Accepts `7.0`, `7.1`, `7.2`, `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`, or `latest`. | +| `wp` | `latest` | Loads the specified WordPress version. Accepts the last three major WordPress versions. As of June 1, 2024, that's `6.3`, `6.4`, or `6.5`. You can also use the generic values `latest`, `nightly`, or `beta`. | +| `blueprint-url` | | The URL of the Blueprint that will be used to configure this Playground instance. | +| `networking` | `no` | Enables or disables the networking support for Playground. Accepts `yes` or `no`. | +| `plugin` | | Installs the specified plugin. Use the plugin name from the WordPress Plugins Directory URL. For example, if the URL is `https://wordpress.org/plugins/wp-lazy-loading/`, the plugin name would be `wp-lazy-loading`. You can pre-install multiple plugins by saying `plugin=coblocks&plugin=wp-lazy-loading&…`. Installing a plugin automatically logs the user in as an admin. | +| `theme` | | Installs the specified theme. Use the theme name from the WordPress Themes Directory URL. For example, if the URL is `https://wordpress.org/themes/disco/`, the theme name would be `disco`. Installing a theme automatically logs the user in as an admin. | +| `url` | `/wp-admin/` | Load the specified initial WordPress page in this Playground instance. | +| `mode` | `browser-full-screen` | Determines how the WordPress instance is displayed. Either wrapped in a browser UI or full width as a seamless experience. Accepts `browser-full-screen`, or `seamless`. | +| `lazy` | | Defer loading the Playground assets until someone clicks on the "Run" button. Does not accept any values. If `lazy` is added as a URL parameter, loading will be deferred. | +| `login` | `yes` | Log the user in as an admin. Accepts `yes` or `no`. | +| `multisite` | `no` | Enables the WordPress multisite mode. Accepts `yes` or `no`. | +| `import-site` | | Imports site files and database from a ZIP file specified by a URL. | +| `import-wxr` | | Imports site content from a WXR file specified by a URL. It uses the WordPress Importer plugin, so the default admin user must be logged in. | +| `site-slug` | | Selects which site to load from browser storage. | +| `language` | `en_US` | Sets the locale for the WordPress instance. This must be used in combination with `networking=yes` otherwise WordPress won't be able to download translations. | +| `core-pr` | | Installs a specific https://github.com/WordPress/wordpress-develop core PR. Accepts the PR number. For example, `core-pr=6883`. | +| `gutenberg-pr` | | Installs a specific https://github.com/WordPress/gutenberg PR. Accepts the PR number. For example, `gutenberg-pr=65337`. | +| `if-stored-site-missing` | | Indicates how to handle the scenario where the `site-slug` parameter identifies a site that does not exist. Use `if-stored-site-missing=prompt` to indicate that the user should be asked whether they would like to save a new site with the specified `site-slug`. | For example, the following code embeds a Playground with a preinstalled Gutenberg plugin and opens the post editor: