@@ -3,11 +3,11 @@ import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
3
3
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker' ;
4
4
import CompilerWorker from '../../src/workers/compiler?worker' ;
5
5
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' ;
8
8
import { API , useAppContext } from '../context' ;
9
9
import { debounce } from '@solid-primitives/scheduled' ;
10
- import { createTabList , defaultTabs , Tab } from '../../src' ;
10
+ import { defaultTabs , Tab } from '../../src' ;
11
11
import type { APIRepl } from './home' ;
12
12
import { Header } from '../components/header' ;
13
13
import { compressToURL } from '@amoutonbrady/lz-string' ;
@@ -28,136 +28,185 @@ const Repl = lazy(() => import('../../src/components/repl'));
28
28
} ,
29
29
} ;
30
30
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' ) ;
32
36
const compiler = new CompilerWorker ( ) ;
33
37
const formatter = new FormatterWorker ( ) ;
34
38
35
39
const params = useParams ( ) ;
36
40
const context = useAppContext ( ) ! ;
41
+ const navigate = useNavigate ( ) ;
37
42
38
- let loaded = false ;
43
+ let disableFetch : true | undefined ;
39
44
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 ) ) ;
41
94
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
+ }
64
126
} 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 ( ) ) ;
66
130
}
67
- } else {
68
- output = await fetch ( `${ API } /repl/${ repl } ` , {
69
- headers : { authorization : context . token ? `Bearer ${ context . token } ` : '' } ,
70
- } ) . then ( ( r ) => r . json ( ) ) ;
71
- }
72
131
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
+ } ) ;
82
142
83
- return output ;
84
- } ) ;
143
+ return output ;
144
+ } ,
145
+ ) ;
85
146
86
- const tabMapper = ( tabs : Tab [ ] ) => tabs . map ( ( x ) => ( { name : x . name , content : x . source . split ( '\n' ) } ) ) ;
87
147
const updateRepl = debounce (
88
148
( ) => {
89
149
const repl = resource . latest ;
90
150
if ( ! repl ) return ;
91
151
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 ( ) ) {
94
155
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
+ } ) ;
96
171
}
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
- } ) ;
113
172
} ,
114
- params . user == 'local' ? 10 : 1000 ,
173
+ ! ! scratchpad ( ) ? 10 : 1000 ,
115
174
) ;
116
175
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
-
123
176
return (
124
177
< >
125
178
< Header
126
179
fork = { ( ) => { } }
127
- share = { ( ) => {
128
- if ( props . scratchpad ) {
180
+ share = { async ( ) => {
181
+ if ( scratchpad ( ) ) {
129
182
let url = new URL ( location . origin ) ;
130
183
url . hash = compressToURL ( JSON . stringify ( context . tabs ( ) ) ) ;
131
184
console . log ( 'Shareable url:' , url . href ) ;
132
185
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
+ }
150
198
} else {
151
- return Promise . resolve ( location . href ) ;
199
+ return location . href ;
152
200
}
153
201
} }
154
202
>
155
- { resource ( ) ?. title && (
203
+ { resource ( ) && (
156
204
< input
157
205
class = "bg-transparent"
158
206
value = { resource ( ) ?. title }
159
207
onChange = { ( e ) => {
160
208
mutate ( ( x ) => x && { ...x , title : e . currentTarget . value } ) ;
209
+ updateRepl ( ) ;
161
210
} }
162
211
/>
163
212
) }
@@ -179,17 +228,19 @@ export const Edit = (props: { horizontal: boolean; scratchpad?: boolean }) => {
179
228
</ svg >
180
229
}
181
230
>
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 >
193
244
</ Suspense >
194
245
</ >
195
246
) ;
0 commit comments