Skip to content

Commit c24cddf

Browse files
committed
better anon, automatic forking
1 parent ffe4a64 commit c24cddf

File tree

12 files changed

+313
-365
lines changed

12 files changed

+313
-365
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@
4545
"register-service-worker": "^1.7.2",
4646
"rollup-plugin-delete": "^2.0.0",
4747
"rollup-plugin-import-css": "^3.0.3",
48-
"rollup-plugin-windicss": "^1.8.5",
48+
"rollup-plugin-windicss": "^1.8.6",
4949
"solid-app-router": "^0.4.1",
5050
"typescript": "^4.7.4",
5151
"vite": "^2.9.13",
5252
"vite-plugin-solid": "^2.2.6",
53-
"vite-plugin-windicss": "^1.8.5",
54-
"windicss": "^3.5.5",
53+
"vite-plugin-windicss": "^1.8.6",
54+
"windicss": "^3.5.6",
5555
"workbox-cli": "^6.5.3"
5656
},
5757
"dependencies": {

playground/app.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ export const App = (): JSX.Element => {
3636
return (
3737
<div class="relative flex bg-white dark:bg-solid-darkbg dark:text-white text-black h-screen text-slate-900 dark:text-slate-50 font-sans flex-col overflow-auto">
3838
<Routes>
39-
<Route path="/scratchpad" element={<Edit horizontal={searchParams.isHorizontal != undefined} scratchpad />} />
40-
<Route path="/:user/:repl" element={<Edit horizontal={searchParams.isHorizontal != undefined} />} />
39+
<Route
40+
path={['/:user/:repl', '/scratchpad']}
41+
element={<Edit horizontal={searchParams.isHorizontal != undefined} />}
42+
/>
4143
<Route path="/:user" element={<Home />} />
4244
<Route path="/" element={<Home />} />
4345
<Route path="/login" element={<Login />} />

playground/pages/edit.tsx

Lines changed: 155 additions & 104 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 { batch, createEffect, createResource, createSignal, lazy, Suspense } from 'solid-js';
7-
import { useParams } from 'solid-app-router';
6+
import { batch, createResource, createSignal, lazy, Show, Suspense } from 'solid-js';
7+
import { useMatch, useNavigate, useParams } from 'solid-app-router';
88
import { API, useAppContext } from '../context';
99
import { debounce } from '@solid-primitives/scheduled';
10-
import { createTabList, defaultTabs, Tab } from '../../src';
10+
import { defaultTabs, Tab } from '../../src';
1111
import type { APIRepl } from './home';
1212
import { Header } from '../components/header';
1313
import { compressToURL } from '@amoutonbrady/lz-string';
@@ -28,136 +28,185 @@ const Repl = lazy(() => import('../../src/components/repl'));
2828
},
2929
};
3030

31-
export const Edit = (props: { horizontal: boolean; scratchpad?: boolean }) => {
31+
interface InternalTab extends Tab {
32+
_source: string;
33+
}
34+
export const Edit = (props: { horizontal: boolean }) => {
35+
const scratchpad = useMatch(() => '/scratchpad');
3236
const compiler = new CompilerWorker();
3337
const formatter = new FormatterWorker();
3438

3539
const params = useParams();
3640
const context = useAppContext()!;
41+
const navigate = useNavigate();
3742

38-
let loaded = false;
43+
let disableFetch: true | undefined;
3944

40-
const [tabs, setTabs] = createTabList([]);
45+
let readonly = () => !scratchpad() && context.user()?.display != params.user;
46+
47+
const mapTabs = (toMap: (Tab | InternalTab)[]): InternalTab[] =>
48+
toMap.map((tab) => {
49+
if ((tab as InternalTab)._source) return tab as InternalTab;
50+
return {
51+
name: tab.name,
52+
_source: tab.source,
53+
get source() {
54+
return this._source;
55+
},
56+
set source(source: string) {
57+
this._source = source;
58+
if (readonly()) {
59+
const myScratchpad = localStorage.getItem('scratchpad');
60+
let output: APIRepl;
61+
if (!myScratchpad) {
62+
output = {
63+
id: 'scratchpad',
64+
title: resource.latest?.title + ' - Forked',
65+
public: true,
66+
version: '1.0',
67+
labels: [],
68+
size: 0,
69+
created_at: new Date().toISOString(),
70+
files: tabs()!.map((x) => ({
71+
name: x.name,
72+
content: x.source.split('\n'),
73+
})),
74+
};
75+
} else {
76+
output = JSON.parse(myScratchpad);
77+
output.files = tabs()!.map((x) => ({
78+
name: x.name,
79+
content: x.source.split('\n'),
80+
}));
81+
}
82+
localStorage.setItem('scratchpad', JSON.stringify(output));
83+
disableFetch = true;
84+
navigate('/scratchpad');
85+
} else {
86+
updateRepl();
87+
}
88+
},
89+
};
90+
});
91+
92+
const [tabs, trueSetTabs] = createSignal<InternalTab[]>([]);
93+
const setTabs = (tabs: (Tab | InternalTab)[]) => trueSetTabs(mapTabs(tabs));
4194
context.setTabs(tabs);
42-
const [current, setCurrent] = createSignal<string>();
43-
const [resource, { mutate }] = createResource<APIRepl, string>(async () => {
44-
const repl = params.repl;
45-
46-
let output: APIRepl;
47-
if (props.scratchpad) {
48-
const scratchpad = localStorage.getItem('scratchpad');
49-
if (!scratchpad) {
50-
output = {
51-
id: 'scratchpad',
52-
title: 'Scratchpad',
53-
public: true,
54-
version: '1.0',
55-
labels: [],
56-
size: 0,
57-
created_at: new Date().toISOString(),
58-
files: defaultTabs.map((x) => ({
59-
name: x.name,
60-
content: x.source.split('\n'),
61-
})),
62-
};
63-
localStorage.setItem('scratchpad', JSON.stringify(output));
95+
96+
const [current, setCurrent] = createSignal<string | undefined>(undefined, { equals: false });
97+
const [resource, { mutate }] = createResource<APIRepl, { repl: string; scratchpad: boolean }>(
98+
() => ({ repl: params.repl, scratchpad: !!scratchpad() }),
99+
async ({ repl, scratchpad }) => {
100+
if (disableFetch) {
101+
disableFetch = undefined;
102+
return resource.latest;
103+
}
104+
105+
let output: APIRepl;
106+
if (scratchpad) {
107+
const myScratchpad = localStorage.getItem('scratchpad');
108+
if (!myScratchpad) {
109+
output = {
110+
id: 'scratchpad',
111+
title: 'Scratchpad',
112+
public: true,
113+
version: '1.0',
114+
labels: [],
115+
size: 0,
116+
created_at: new Date().toISOString(),
117+
files: defaultTabs.map((x) => ({
118+
name: x.name,
119+
content: x.source.split('\n'),
120+
})),
121+
};
122+
localStorage.setItem('scratchpad', JSON.stringify(output));
123+
} else {
124+
output = JSON.parse(myScratchpad);
125+
}
64126
} else {
65-
output = JSON.parse(scratchpad);
127+
output = await fetch(`${API}/repl/${repl}`, {
128+
headers: { authorization: context.token ? `Bearer ${context.token}` : '' },
129+
}).then((r) => r.json());
66130
}
67-
} else {
68-
output = await fetch(`${API}/repl/${repl}`, {
69-
headers: { authorization: context.token ? `Bearer ${context.token}` : '' },
70-
}).then((r) => r.json());
71-
}
72131

73-
batch(() => {
74-
setTabs(
75-
output.files.map((x) => {
76-
return { name: x.name, source: x.content.join('\n') };
77-
}),
78-
);
79-
setCurrent(output.files[0].name);
80-
});
81-
loaded = true;
132+
console.log('refetched');
133+
134+
batch(() => {
135+
setTabs(
136+
output.files.map((x) => {
137+
return { name: x.name, source: x.content.join('\n') };
138+
}),
139+
);
140+
setCurrent(output.files[0].name);
141+
});
82142

83-
return output;
84-
});
143+
return output;
144+
},
145+
);
85146

86-
const tabMapper = (tabs: Tab[]) => tabs.map((x) => ({ name: x.name, content: x.source.split('\n') }));
87147
const updateRepl = debounce(
88148
() => {
89149
const repl = resource.latest;
90150
if (!repl) return;
91151

92-
const files = tabMapper(tabs());
93-
if (props.scratchpad) {
152+
const files = tabs().map((x) => ({ name: x.name, content: x.source.split('\n') }));
153+
154+
if (scratchpad()) {
94155
localStorage.setItem('scratchpad', JSON.stringify({ ...repl, files }));
95-
return;
156+
} else if (context.token && context.user()?.display == params.user) {
157+
fetch(`${API}/repl/${params.repl}`, {
158+
method: 'PUT',
159+
headers: {
160+
'authorization': `Bearer ${context.token}`,
161+
'Content-Type': 'application/json',
162+
},
163+
body: JSON.stringify({
164+
title: repl.title,
165+
version: repl.version,
166+
public: repl.public,
167+
labels: repl.labels,
168+
files,
169+
}),
170+
});
96171
}
97-
98-
if (!context.token || context.user()?.display != params.user) return;
99-
fetch(`${API}/repl/${params.repl}`, {
100-
method: 'PUT',
101-
headers: {
102-
'authorization': `Bearer ${context.token}`,
103-
'Content-Type': 'application/json',
104-
},
105-
body: JSON.stringify({
106-
title: repl.title,
107-
version: repl.version,
108-
public: repl.public,
109-
labels: repl.labels,
110-
files,
111-
}),
112-
});
113172
},
114-
params.user == 'local' ? 10 : 1000,
173+
!!scratchpad() ? 10 : 1000,
115174
);
116175

117-
createEffect(() => {
118-
tabMapper(tabs()); // use the latest value on debounce, and just throw this value away (but use it to track)
119-
resource();
120-
if (loaded) updateRepl();
121-
});
122-
123176
return (
124177
<>
125178
<Header
126179
fork={() => {}}
127-
share={() => {
128-
if (props.scratchpad) {
180+
share={async () => {
181+
if (scratchpad()) {
129182
let url = new URL(location.origin);
130183
url.hash = compressToURL(JSON.stringify(context.tabs()));
131184
console.log('Shareable url:', url.href);
132185

133-
return fetch('/', { method: 'PUT', body: `{"url":"${url.href}"}` })
134-
.then((response) => {
135-
if (response.status >= 400) {
136-
throw new Error(response.statusText);
137-
}
138-
139-
return response.text();
140-
})
141-
.then((hash) => {
142-
const tinyUrl = new URL(location.origin);
143-
tinyUrl.searchParams.set('hash', hash);
144-
145-
return tinyUrl.toString();
146-
})
147-
.catch(() => {
148-
return url.href;
149-
});
186+
try {
187+
const response = await fetch('/', { method: 'PUT', body: `{"url":"${url.href}"}` });
188+
if (response.status >= 400) {
189+
throw new Error(response.statusText);
190+
}
191+
const hash = await response.text();
192+
const tinyUrl = new URL(location.origin);
193+
tinyUrl.searchParams.set('hash', hash);
194+
return tinyUrl.toString();
195+
} catch {
196+
return url.href;
197+
}
150198
} else {
151-
return Promise.resolve(location.href);
199+
return location.href;
152200
}
153201
}}
154202
>
155-
{resource()?.title && (
203+
{resource() && (
156204
<input
157205
class="bg-transparent"
158206
value={resource()?.title}
159207
onChange={(e) => {
160208
mutate((x) => x && { ...x, title: e.currentTarget.value });
209+
updateRepl();
161210
}}
162211
/>
163212
)}
@@ -179,17 +228,19 @@ export const Edit = (props: { horizontal: boolean; scratchpad?: boolean }) => {
179228
</svg>
180229
}
181230
>
182-
<Repl
183-
compiler={compiler}
184-
formatter={formatter}
185-
isHorizontal={props.horizontal}
186-
dark={context.dark()}
187-
tabs={tabs()}
188-
setTabs={setTabs}
189-
current={current()}
190-
setCurrent={setCurrent}
191-
id="repl"
192-
/>
231+
<Show when={resource()}>
232+
<Repl
233+
compiler={compiler}
234+
formatter={formatter}
235+
isHorizontal={props.horizontal}
236+
dark={context.dark()}
237+
tabs={tabs()}
238+
setTabs={setTabs}
239+
current={current()}
240+
setCurrent={setCurrent}
241+
id={'repl'}
242+
/>
243+
</Show>
193244
</Suspense>
194245
</>
195246
);

0 commit comments

Comments
 (0)