diff --git a/package.json b/package.json
index a4a8f8e58..2fab0cf2a 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"rules": {
"react/sort-comp": 0,
"react/no-danger": 0,
+ "react/jsx-no-bind": 0,
"brace-style": 0,
"indent": 0,
"lines-around-comment": 0,
diff --git a/src/components/controllers/repl-page.jsx b/src/components/controllers/repl-page.jsx
index 11854998e..888126769 100644
--- a/src/components/controllers/repl-page.jsx
+++ b/src/components/controllers/repl-page.jsx
@@ -1,6 +1,6 @@
-import { useRoute } from 'preact-iso';
+import { useLocation, useRoute } from 'preact-iso';
import { Repl } from './repl';
-import { useExample } from './repl/examples';
+import { fetchExample } from './repl/examples';
import { useContent, useResource } from '../../lib/use-resource';
import { useTitle, useDescription } from './utils';
import { useLanguage } from '../../lib/i18n';
@@ -15,7 +15,7 @@ export default function ReplPage() {
useTitle(meta.title);
useDescription(meta.description);
- const [code, slug] = initialCode(query);
+ const code = useResource(() => getInitialCode(query), [query]);
return (
@@ -24,11 +24,8 @@ export default function ReplPage() {
height: 100% !important;
overflow: hidden !important;
}
- footer {
- display: none !important;
- }
`}
-
+
);
}
@@ -38,42 +35,31 @@ export default function ReplPage() {
*
* ?code -> ?example -> localStorage -> simple counter example
*/
-function initialCode(query) {
- let code, slug;
+async function getInitialCode(query) {
+ const { route } = useLocation();
+ let code;
if (query.code) {
- try {
- code = useResource(() => querySafetyCheck(atob(query.code)), [query.code]);
- } catch (e) {}
+ code = querySafetyCheck(atob(query.code));
} else if (query.example) {
- code = useExample([query.example]);
- if (code) {
- slug = query.example;
- history.replaceState(
- null,
- null,
- `/repl?example=${encodeURIComponent(slug)}`
- );
+ code = await fetchExample(query.example);
+ if (!code) {
+ route('/repl', true);
}
- else history.replaceState(null, null, '/repl');
}
if (!code) {
if (typeof window !== 'undefined' && localStorage.getItem('preact-www-repl-code')) {
code = localStorage.getItem('preact-www-repl-code');
} else {
- slug = 'counter';
+ const slug = 'counter';
if (typeof window !== 'undefined') {
- history.replaceState(
- null,
- null,
- `/repl?example=${encodeURIComponent(slug)}`
- );
+ route(`/repl?example=${encodeURIComponent(slug)}`, true);
}
- code = useExample([slug]);
+ code = await fetchExample(slug);
}
}
- return [code, slug];
+ return code;
}
async function querySafetyCheck(code) {
diff --git a/src/components/controllers/repl/examples.js b/src/components/controllers/repl/examples.js
index 26a5f0512..387eb8ff7 100644
--- a/src/components/controllers/repl/examples.js
+++ b/src/components/controllers/repl/examples.js
@@ -1,5 +1,3 @@
-import { useResource } from '../../../lib/use-resource';
-
import simpleCounterExample from './examples/simple-counter.txt?url';
import counterWithHtmExample from './examples/counter-with-htm.txt?url';
import todoExample from './examples/todo-list.txt?url';
@@ -69,15 +67,10 @@ export function getExample(slug, list = EXAMPLES) {
}
/**
- * @param {[ slug: string ]} args
- * @returns {string | undefined}
+ * @param {string} slug
*/
-export function useExample([slug]) {
+export async function fetchExample(slug) {
const example = getExample(slug);
if (!example) return;
- return useResource(() => loadExample(example.url), ['example', slug]);
-}
-
-export async function loadExample(exampleUrl) {
- return await fetch(exampleUrl).then(r => r.text());
+ return await fetch(example.url).then(r => r.text());
}
diff --git a/src/components/controllers/repl/index.jsx b/src/components/controllers/repl/index.jsx
index 5b91ae557..ac793990c 100644
--- a/src/components/controllers/repl/index.jsx
+++ b/src/components/controllers/repl/index.jsx
@@ -1,6 +1,7 @@
import { useState, useEffect } from 'preact/hooks';
+import { useLocation, useRoute } from 'preact-iso';
import { Splitter } from '../../splitter';
-import { EXAMPLES, getExample, loadExample } from './examples';
+import { EXAMPLES, fetchExample } from './examples';
import { ErrorOverlay } from './error-overlay';
import { useStoredValue } from '../../../lib/localstorage';
import { useResource } from '../../../lib/use-resource';
@@ -13,9 +14,10 @@ import REPL_CSS from './examples.css?raw';
* @param {string} props.code
* @param {string} [props.slug]
*/
-export function Repl({ code, slug }) {
+export function Repl({ code }) {
+ const { route } = useLocation();
+ const { query } = useRoute();
const [editorCode, setEditorCode] = useStoredValue('preact-www-repl-code', code);
- const [exampleSlug, setExampleSlug] = useState(slug || '');
const [error, setError] = useState(null);
const [copied, setCopied] = useState(false);
@@ -23,6 +25,9 @@ export function Repl({ code, slug }) {
// causes some bad jumping/pop-in. For the moment, this is the best option
if (typeof window === 'undefined') return null;
+ /**
+ * @type {{ Runner: import('../repl/runner').default, CodeEditor: import('../../code-editor').default }}
+ */
const { Runner, CodeEditor } = useResource(() => Promise.all([
import('../../code-editor'),
import('./runner')
@@ -30,38 +35,30 @@ export function Repl({ code, slug }) {
const applyExample = (e) => {
const slug = e.target.value;
- loadExample(getExample(slug).url)
+ fetchExample(slug)
.then(code => {
setEditorCode(code);
- setExampleSlug(slug);
- history.replaceState(
- null,
- null,
- `/repl?example=${encodeURIComponent(slug)}`
- );
+ route(`/repl?example=${encodeURIComponent(slug)}`, true);
});
};
- useEffect(() => {
- const example = getExample(exampleSlug);
- (async function () {
- if (example) {
- const code = await loadExample(example.url);
- if (location.search && code !== editorCode) {
- setExampleSlug('');
- history.replaceState(null, null, '/repl');
- }
- }
- })();
- }, [editorCode]);
+ const onEditorInput = (code) => {
+ setEditorCode(code);
+
+ // Clears the (now outdated) example & code query params
+ // when a user begins to modify the code
+ if (location.search) {
+ route('/repl', true);
+ }
+ };
const share = () => {
- if (!exampleSlug) {
- history.replaceState(
- null,
- null,
- `/repl?code=${encodeURIComponent(btoa(editorCode))}`
- );
+ // No reason to share semi-sketchy btoa'd code if there's
+ // a perfectly good example we can use instead
+ if (!query.example) {
+ // We use `history.replaceState` here as the code is only relevant on mount.
+ // There's no need to notify the router of the change.
+ history.replaceState(null, null, `/repl?code=${encodeURIComponent(btoa(editorCode))}`);
}
try {
@@ -90,13 +87,13 @@ export function Repl({ code, slug }) {