Skip to content

Commit 9084f1d

Browse files
committed
fix a suspense bug
1 parent 3b50548 commit 9084f1d

File tree

2 files changed

+75
-27
lines changed

2 files changed

+75
-27
lines changed

playground/pages/edit.tsx

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
33
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
44
import CompilerWorker from '../../src/workers/compiler?worker';
55
import FormatterWorker from '../../src/workers/formatter?worker';
6-
import { createTabList } from '../../src';
76
import { createEffect, createResource, createSignal, lazy, Suspense } from 'solid-js';
87
import { useParams } from 'solid-app-router';
98
import { API, useAppContext } from '../context';
109
import createDebounce from '@solid-primitives/debounce';
10+
import type { Tab } from '../../src';
1111
import type { APIRepl } from './home';
1212

1313
const Repl = lazy(() => import('../../src/components/repl'));
@@ -26,53 +26,97 @@ const Repl = lazy(() => import('../../src/components/repl'));
2626
},
2727
};
2828

29+
// Custom version of createTabList that allows us to use a resource as the backing signal
30+
const createTabList = () => {
31+
let sourceSignals: Record<string, [get: () => string, set: (value: string) => string]> = {};
32+
33+
const mapTabs = (tabs: Tab[]): Tab[] => {
34+
const oldSignals = sourceSignals;
35+
sourceSignals = {};
36+
37+
return tabs.map((tab) => {
38+
const id = `${tab.name}.${tab.type}`;
39+
sourceSignals[id] = oldSignals[id] || createSignal(tab.source);
40+
if (oldSignals[id]) oldSignals[id][1](tab.source);
41+
42+
return {
43+
name: tab.name,
44+
type: tab.type,
45+
get source() {
46+
return sourceSignals[id][0]();
47+
},
48+
set source(source: string) {
49+
sourceSignals[id][1](source);
50+
},
51+
};
52+
});
53+
};
54+
55+
return mapTabs;
56+
};
57+
2958
export const Edit = (props: { dark: boolean; horizontal: boolean }) => {
3059
const compiler = new CompilerWorker();
3160
const formatter = new FormatterWorker();
3261

3362
const params = useParams();
3463
const context = useAppContext()!;
35-
const [fetchedTabs] = createResource<APIRepl, string>(params.repl, (repl) =>
36-
fetch(`${API}/repl/${repl}`).then((r) => r.json()),
37-
);
3864

39-
const [tabs, setTabs] = createTabList([]);
65+
const tabMapper = (tabs: Tab[]) => tabs.map((x) => ({ name: `${x.name}.${x.type}`, content: x.source.split('\n') }));
66+
const mapTabs = createTabList();
67+
const [resource, { mutate }] = createResource<{ tabs: Tab[]; repl: APIRepl }, string>(params.repl, async (repl) => {
68+
let x: APIRepl = await fetch(`${API}/repl/${repl}`, {
69+
headers: { authorization: `Bearer ${context.token}` },
70+
}).then((r) => r.json());
71+
72+
return {
73+
repl: x,
74+
tabs: mapTabs(
75+
x.files.map((x) => {
76+
let dot = x.name.lastIndexOf('.');
77+
return { name: x.name.slice(0, dot), type: x.name.slice(dot + 1), source: x.content.join('\n') };
78+
}),
79+
),
80+
};
81+
});
82+
4083
const [current, setCurrent] = createSignal<string>();
4184
createEffect(() => {
42-
const myRepl = fetchedTabs();
85+
const myRepl = resource();
4386
if (!myRepl) return;
44-
setTabs(
45-
myRepl.files.map((x) => {
46-
let dot = x.name.lastIndexOf('.');
47-
return { name: x.name.slice(0, dot), type: x.name.slice(dot + 1), source: x.content.join('\n') };
48-
}),
49-
);
50-
setCurrent(myRepl.files[0].name);
87+
setCurrent(`${myRepl.tabs[0].name}.${myRepl.tabs[0].type}`);
5188
});
5289

53-
const tabMapper = () => tabs().map((x) => ({ name: `${x.name}.${x.type}`, content: x.source.split('\n') }));
90+
const tabs = () => resource()?.tabs || [];
91+
const setTabs = (tabs: Tab[]) => {
92+
if (resource.latest) mutate({ repl: resource.latest.repl, tabs: mapTabs(tabs) });
93+
};
94+
5495
const updateRepl = createDebounce(() => {
55-
const repl = fetchedTabs();
56-
const tabs = tabMapper();
57-
if (!repl || !tabs.length) return;
58-
fetch(`${API}/repl/${repl.id}`, {
96+
const repl = resource();
97+
if (!repl) return;
98+
const tabs = tabMapper(repl.tabs);
99+
fetch(`${API}/repl/${params.repl}`, {
59100
method: 'PUT',
60101
headers: {
61102
authorization: `Bearer ${context.token}`,
62103
'Content-Type': 'application/json',
63104
},
64105
body: JSON.stringify({
65-
title: repl.title,
66-
version: repl.version,
67-
public: repl.public,
68-
labels: repl.labels,
106+
title: repl.repl.title,
107+
version: repl.repl.version,
108+
public: repl.repl.public,
109+
labels: repl.repl.labels,
69110
files: tabs,
70111
}),
71112
});
72113
}, 1000);
114+
115+
let firstRun = true;
73116
createEffect(() => {
74-
tabMapper();
75-
updateRepl();
117+
tabMapper(tabs()); // use the latest value on debounce, and just throw this value away (but use it to track)
118+
if (firstRun) firstRun = false;
119+
else updateRepl();
76120
});
77121

78122
return (

src/components/repl.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ const Repl: ReplProps = (props) => {
3535
const [store, setStore] = createStore({
3636
error: '',
3737
compiled: '',
38-
compiledTabs: {
39-
[`./${props.current}`]: '',
40-
},
38+
compiledTabs: props.current
39+
? {
40+
[`./${props.current}`]: '',
41+
}
42+
: {},
4143
mode: 'DOM' as keyof typeof compileMode,
4244
isCompiling: false,
4345
get compileMode(): ValueOf<typeof compileMode> {
@@ -155,6 +157,8 @@ const Repl: ReplProps = (props) => {
155157
* Also, real time feedback can be stressful
156158
*/
157159
const applyCompilation = debounce((tabs: Tab[], compileOpts: Record<string, any>) => {
160+
if (!tabs.length) return;
161+
158162
setStore('isCompiling', true);
159163
now = performance.now();
160164

0 commit comments

Comments
 (0)