1
- import React , {
2
- createContext ,
3
- useCallback ,
4
- useContext ,
5
- useEffect ,
6
- useReducer ,
7
- useRef ,
8
- useState ,
9
- } from 'react' ;
1
+ import React , { createContext , useCallback , useContext , useReducer , useRef , useState } from 'react' ;
10
2
11
- import type {
12
- FilePayloadType ,
13
- FileType ,
14
- DirEntryType ,
15
- FileEntryType ,
16
- AppType ,
17
- } from '@srcbook/shared' ;
3
+ import type { FileType , DirEntryType , FileEntryType , AppType } from '@srcbook/shared' ;
18
4
import { AppChannel } from '@/clients/websocket' ;
19
5
import {
20
6
createFile as doCreateFile ,
@@ -35,23 +21,7 @@ import {
35
21
updateFileNode ,
36
22
} from './lib/file-tree' ;
37
23
38
- function removeCachedFiles (
39
- files : Record < string , FileType > ,
40
- path : string ,
41
- ) : Record < string , FileType > {
42
- const result : Record < string , FileType > = { } ;
43
-
44
- for ( const key of Object . keys ( files ) ) {
45
- if ( ! key . startsWith ( path ) ) {
46
- result [ key ] = files [ key ] ! ;
47
- }
48
- }
49
-
50
- return result ;
51
- }
52
-
53
24
export interface FilesContextValue {
54
- files : FileType [ ] ;
55
25
fileTree : DirEntryType ;
56
26
openFile : ( entry : FileEntryType ) => void ;
57
27
createFile : ( dirname : string , basename : string , source ?: string ) => void ;
@@ -85,25 +55,14 @@ export function FilesProvider({ app, channel, rootDirEntries, children }: Provid
85
55
//
86
56
const [ , forceComponentRerender ] = useReducer ( ( x ) => x + 1 , 0 ) ;
87
57
88
- const filesRef = useRef < Record < string , FileType > > ( { } ) ;
89
58
const fileTreeRef = useRef < DirEntryType > ( sortTree ( rootDirEntries ) ) ;
90
59
const openedDirectoriesRef = useRef < Set < string > > ( new Set ( ) ) ;
91
60
92
61
const [ openedFile , setOpenedFile ] = useState < FileType | null > ( null ) ;
93
62
94
- useEffect ( ( ) => {
95
- function onFile ( payload : FilePayloadType ) {
96
- filesRef . current [ payload . file . path ] = payload . file ;
97
- forceComponentRerender ( ) ;
98
- }
99
- channel . on ( 'file' , onFile ) ;
100
- return ( ) => channel . off ( 'file' , onFile ) ;
101
- } , [ channel , forceComponentRerender ] ) ;
102
-
103
63
const openFile = useCallback (
104
64
async ( entry : FileEntryType ) => {
105
65
const { data : file } = await loadFile ( app . id , entry . path ) ;
106
- filesRef . current [ file . path ] = file ;
107
66
setOpenedFile ( file ) ;
108
67
} ,
109
68
[ app . id ] ,
@@ -123,8 +82,8 @@ export function FilesProvider({ app, channel, rootDirEntries, children }: Provid
123
82
const updateFile = useCallback (
124
83
( file : FileType , attrs : Partial < FileType > ) => {
125
84
const updatedFile : FileType = { ...file , ...attrs } ;
126
- filesRef . current [ file . path ] = updatedFile ;
127
85
channel . push ( 'file:updated' , { file : updatedFile } ) ;
86
+ setOpenedFile ( updatedFile ) ;
128
87
forceComponentRerender ( ) ;
129
88
} ,
130
89
[ channel ] ,
@@ -133,7 +92,6 @@ export function FilesProvider({ app, channel, rootDirEntries, children }: Provid
133
92
const deleteFile = useCallback (
134
93
async ( entry : FileEntryType ) => {
135
94
await doDeleteFile ( app . id , entry . path ) ;
136
- delete filesRef . current [ entry . path ] ;
137
95
setOpenedFile ( ( openedFile ) => {
138
96
if ( openedFile && openedFile . path === entry . path ) {
139
97
return null ;
@@ -143,28 +101,19 @@ export function FilesProvider({ app, channel, rootDirEntries, children }: Provid
143
101
fileTreeRef . current = deleteNode ( fileTreeRef . current , entry . path ) ;
144
102
forceComponentRerender ( ) ; // required
145
103
} ,
146
- [ app . id ] ,
104
+ [ app . id , openedFile ] ,
147
105
) ;
148
106
149
107
const renameFile = useCallback (
150
108
async ( entry : FileEntryType , name : string ) => {
151
109
const { data : newEntry } = await doRenameFile ( app . id , entry . path , name ) ;
152
- const oldFile = filesRef . current [ entry . path ] ;
153
- if ( oldFile ) {
154
- const newFile = { ...oldFile , path : newEntry . path , name : newEntry . name } ;
155
- delete filesRef . current [ oldFile . path ] ;
156
- filesRef . current [ newFile . path ] = newFile ;
157
- setOpenedFile ( ( openedFile ) => {
158
- if ( openedFile && openedFile . path === oldFile . path ) {
159
- return newFile ;
160
- }
161
- return openedFile ;
162
- } ) ;
110
+ if ( openedFile && openedFile . path === entry . path ) {
111
+ setOpenedFile ( { ...openedFile , path : newEntry . path , name : newEntry . name } ) ;
163
112
}
164
113
fileTreeRef . current = updateFileNode ( fileTreeRef . current , entry , newEntry ) ;
165
114
forceComponentRerender ( ) ; // required
166
115
} ,
167
- [ app . id ] ,
116
+ [ app . id , openedFile ] ,
168
117
) ;
169
118
170
119
const isFolderOpen = useCallback ( ( entry : DirEntryType ) => {
@@ -212,50 +161,37 @@ export function FilesProvider({ app, channel, rootDirEntries, children }: Provid
212
161
const deleteFolder = useCallback (
213
162
async ( entry : DirEntryType ) => {
214
163
await deleteDirectory ( app . id , entry . path ) ;
215
- removeCachedFiles ( filesRef . current , entry . path ) ;
216
- setOpenedFile ( ( openedFile ) => {
217
- if ( openedFile && openedFile . path . startsWith ( entry . path ) ) {
218
- return null ;
219
- }
220
- return openedFile ;
221
- } ) ;
164
+ if ( openedFile && openedFile . path . startsWith ( entry . path ) ) {
165
+ setOpenedFile ( null ) ;
166
+ }
222
167
openedDirectoriesRef . current . delete ( entry . path ) ;
223
168
fileTreeRef . current = deleteNode ( fileTreeRef . current , entry . path ) ;
224
169
forceComponentRerender ( ) ; // required
225
170
} ,
226
- [ app . id ] ,
171
+ [ app . id , openedFile ] ,
227
172
) ;
228
173
229
174
const renameFolder = useCallback (
230
175
async ( entry : DirEntryType , name : string ) => {
231
176
const { data : newEntry } = await renameDirectory ( app . id , entry . path , name ) ;
232
- // const oldFile = filesRef.current[entry.path];
233
- // if (oldFile) {
234
- // const newFile = { ...oldFile, path: newEntry.path, name: newEntry.name };
235
- // delete filesRef.current[oldFile.path];
236
- // filesRef.current[newFile.path] = newFile;
237
- // setOpenedFile((openedFile) => {
238
- // if (openedFile && openedFile.path === oldFile.path) {
239
- // return newFile;
240
- // }
241
- // return openedFile;
242
- // });
243
- // }
177
+
178
+ if ( openedFile && openedFile . path . startsWith ( entry . path ) ) {
179
+ setOpenedFile ( { ...openedFile , path : openedFile . path . replace ( entry . path , newEntry . path ) } ) ;
180
+ }
181
+
244
182
if ( openedDirectoriesRef . current . has ( entry . path ) ) {
245
183
openedDirectoriesRef . current . delete ( entry . path ) ;
246
184
openedDirectoriesRef . current . add ( newEntry . path ) ;
247
185
}
248
186
249
187
fileTreeRef . current = renameDirNode ( fileTreeRef . current , entry , newEntry ) ;
188
+
250
189
forceComponentRerender ( ) ; // required
251
190
} ,
252
- [ app . id ] ,
191
+ [ app . id , openedFile ] ,
253
192
) ;
254
193
255
- const files = Object . values ( filesRef . current ) ;
256
-
257
194
const context : FilesContextValue = {
258
- files,
259
195
fileTree : fileTreeRef . current ,
260
196
openFile,
261
197
renameFile,
0 commit comments