@@ -3,6 +3,7 @@ import React, {
33 lazy ,
44 memo ,
55 Suspense ,
6+ useCallback ,
67 useEffect ,
78 useImperativeHandle ,
89 useMemo ,
@@ -12,7 +13,8 @@ import React, {
1213import { useInView } from 'react-intersection-observer' ;
1314import { Tooltip } from 'react-tooltip' ;
1415import FigureLabels from '../common/FigureLabels' ;
15- import FileUploader from '../common/FileUploader' ;
16+ import FileUploader from '../common/FileUploader' ;
17+ import { useSearchRealm } from '../common/Storage' ;
1618import BrowserEditor from './BrowserEditor' ;
1719import FolderStructure from './FolderStructure' ;
1820import MenuBar from './MenuBar' ;
@@ -53,32 +55,27 @@ function Figure(props) {
5355}
5456
5557
56- const ScrollSpy = ( props ) => {
57- const { fetchFiles } = props ;
58+ function ScrollSpy ( props ) {
59+ const { setDirty } = props ;
5860 const { ref, inView} = useInView ( {
5961 triggerOnce : true ,
60- onChange : ( loadMore ) => {
62+ onChange : async ( loadMore ) => {
6163 if ( loadMore && ! inView ) {
62- fetchFiles ( ) ;
64+ setDirty ( true ) ;
6365 }
6466 } ,
6567 } ) ;
6668 console . log ( 'ScrollSpy' , inView ) ;
67- if ( inView ) {
68- console . log ( 'already visible' ) ;
69- fetchFiles ( ) ;
70- }
71-
7269 return (
7370 < div className = "scroll-spy" ref = { ref } > </ div >
7471 ) ;
75- } ;
72+ }
7673
7774
7875const FilesList = memo ( ( props : any ) => {
79- const { structure, fetchFiles , selectFile} = props ;
76+ const { structure, setDirty , selectFile} = props ;
8077
81- console . log ( 'FolderList ' , structure ) ;
78+ console . log ( 'FilesList ' , structure ) ;
8279
8380 return (
8481 < ul className = "files-browser" > {
@@ -87,14 +84,14 @@ const FilesList = memo((props: any) => {
8784 < > { structure . files . map ( file => (
8885 < li key = { file . id } onClick = { ( ) => selectFile ( file ) } > < Figure { ...file } /> </ li >
8986 ) ) }
90- { structure . offset !== null && < ScrollSpy fetchFiles = { fetchFiles } /> }
87+ { structure . offset !== null && < ScrollSpy key = { structure . offset } setDirty = { setDirty } /> }
9188 </ >
9289 ) } </ ul >
9390 ) ;
9491} ) ;
9592
9693
97- const FileSelectDialog = forwardRef ( ( props : any , forwardedRef ) => {
94+ const FileSelectDialog = forwardRef ( ( props : any , forwardedRef ) => {
9895 const { realm, baseUrl, csrfToken} = props ;
9996 const [ structure , setStructure ] = useState ( {
10097 root_folder : null ,
@@ -105,79 +102,82 @@ const FileSelectDialog = forwardRef((props: any, forwardedRef) => {
105102 search_query : '' ,
106103 labels : [ ] ,
107104 } ) ;
105+ const [ isDirty , setDirty ] = useState ( false ) ;
108106 const ref = useRef ( null ) ;
109107 const uploaderRef = useRef ( null ) ;
108+ const menuBarRef = useRef ( null ) ;
110109 const [ uploadedFile , setUploadedFile ] = useState ( null ) ;
111- const dialog = ref . current ?. closest ( 'dialog' ) ;
110+ const [ currentFolderElement , setCurrentFolderElement ] = useState ( null ) ;
111+ const [ searchRealm , setSearchRealm ] = useSearchRealm ( 'current' ) ;
112+
113+ useImperativeHandle ( forwardedRef , ( ) => ( { scrollToCurrentFolder, dismissAndClose} ) ) ;
112114
113115 useEffect ( ( ) => {
114- if ( ! uploadedFile ) {
115- getStructure ( ) ;
116+ if ( structure . root_folder === null ) {
117+ initializeStructure ( ) ;
118+ } else if ( isDirty ) {
119+ fetchFiles ( ) ;
116120 }
117- } , [ uploadedFile ] ) ;
118-
119- useImperativeHandle ( forwardedRef , ( ) => ( { dismissAndClose} ) ) ;
121+ } , [ isDirty ] ) ;
120122
121- const setCurrentFolder = ( folderId ) => {
122- setStructure ( prevStructure => {
123- const newStructure = Object . assign ( structure , {
124- ...prevStructure ,
125- last_folder : folderId ,
126- files : [ ] ,
127- offset : null ,
128- recursive : false ,
129- search_query : '' ,
130- } ) ;
131- fetchFiles ( ) ;
132- return newStructure ;
123+ function setCurrentFolder ( folderId ) {
124+ setStructure ( {
125+ ...structure ,
126+ last_folder : folderId ,
127+ files : [ ] ,
128+ offset : null ,
129+ recursive : false ,
130+ search_query : '' ,
133131 } ) ;
134- } ;
132+ setDirty ( true ) ;
133+ menuBarRef . current . clearSearch ( ) ;
134+ }
135135
136- async function toggleRecursive ( folderId : string ) {
137- setStructure ( prevStructure => {
138- const newStructure = Object . assign ( structure , {
139- ...prevStructure ,
140- last_folder : folderId ,
141- files : [ ] ,
142- offset : null ,
143- recursive : prevStructure . recursive ? false : true ,
144- search_query : '' ,
145- } ) ;
146- fetchFiles ( ) ;
147- return newStructure ;
136+ function refreshFilesList ( ) {
137+ setStructure ( {
138+ ...structure ,
139+ files : [ ] ,
140+ offset : null ,
148141 } ) ;
142+ setDirty ( true ) ;
149143 }
150144
151- const setSearchQuery = ( query ) => {
152- setStructure ( prevStructure => {
153- const newStructure = Object . assign ( structure , {
154- ...prevStructure ,
145+ function changeSearchRealm ( value ) {
146+ if ( value !== searchRealm ) {
147+ setSearchRealm ( value ) ;
148+ setStructure ( {
149+ ...structure ,
155150 files : [ ] ,
156151 offset : null ,
157- recursive : false ,
158- search_query : query ,
159152 } ) ;
160- fetchFiles ( ) ;
161- return newStructure ;
153+ setDirty ( true ) ;
154+ }
155+ }
156+
157+ function toggleRecursive ( folderId : string ) {
158+ setStructure ( {
159+ ...structure ,
160+ last_folder : folderId ,
161+ files : [ ] ,
162+ offset : null ,
163+ recursive : structure . recursive ? false : true ,
162164 } ) ;
163- } ;
165+ setDirty ( true ) ;
166+ }
164167
165- const refreshFilesList = ( ) => {
166- setStructure ( prevStructure => {
167- const newStructure = Object . assign ( structure , {
168- root_folder : prevStructure . root_folder ,
169- files : [ ] ,
170- last_folder : prevStructure . last_folder ,
171- offset : null ,
172- search_query : prevStructure . search_query ,
173- labels : prevStructure . labels ,
174- } ) ;
175- fetchFiles ( ) ;
176- return newStructure ;
168+ function setSearchQuery ( query ) {
169+ setStructure ( {
170+ ...structure ,
171+ files : [ ] ,
172+ offset : null ,
173+ recursive : false ,
174+ search_query : query ,
177175 } ) ;
178- } ;
176+ setDirty ( true ) ;
177+ }
179178
180- async function getStructure ( ) {
179+ async function initializeStructure ( ) {
180+ setDirty ( false ) ;
181181 const response = await fetch ( `${ baseUrl } structure/${ realm } ` ) ;
182182 if ( response . ok ) {
183183 setStructure ( await response . json ( ) ) ;
@@ -199,7 +199,7 @@ const FileSelectDialog = forwardRef((props: any, forwardedRef) => {
199199 params . set ( 'q' , structure . search_query ) ;
200200 return `${ baseUrl } ${ structure . last_folder } /search?${ params . toString ( ) } ` ;
201201 }
202- return `${ baseUrl } ${ structure . last_folder } /list? ${ params . toString ( ) } ` ;
202+ return `${ baseUrl } ${ structure . last_folder } /list${ params . size === 0 ? '' : `? ${ params . toString ( ) } ` } ` ;
203203 } ) ( ) ;
204204 const response = await fetch ( fetchUrl ) ;
205205 if ( response . ok ) {
@@ -209,23 +209,29 @@ const FileSelectDialog = forwardRef((props: any, forwardedRef) => {
209209 files : structure . files . concat ( body . files ) ,
210210 offset : body . offset ,
211211 } ) ;
212+ setDirty ( false ) ;
212213 } else {
213214 console . error ( response ) ;
214215 }
215216 }
216217
217- function refreshStructure ( ) {
218- setStructure ( { ...structure } ) ;
219- }
220-
221- function handleUpload ( folderId , uploadedFiles ) {
218+ function handleUpload ( folderId , uploadedFiles ) {
219+ if ( structure . last_folder !== folderId )
220+ throw new Error ( 'Folder mismatch' ) ;
222221 setUploadedFile ( uploadedFiles [ 0 ] ) ;
223222 }
224223
225- function selectFile ( fileInfo ) {
224+ const selectFile = useCallback ( fileInfo => {
226225 props . selectFile ( fileInfo ) ;
227226 setUploadedFile ( null ) ;
227+ refreshFilesList ( ) ;
228228 props . closeDialog ( ) ;
229+ } , [ ] ) ;
230+
231+ function scrollToCurrentFolder ( ) {
232+ if ( currentFolderElement ) {
233+ currentFolderElement . scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ;
234+ }
229235 }
230236
231237 function dismissAndClose ( ) {
@@ -248,50 +254,57 @@ const FileSelectDialog = forwardRef((props: any, forwardedRef) => {
248254 props . closeDialog ( ) ;
249255 }
250256
257+ console . log ( 'FileSelectDialog' , isDirty , structure ) ;
258+
251259 return ( < >
252260 < div className = "wrapper" ref = { ref } >
253261 { uploadedFile ?
254- < BrowserEditor
255- uploadedFile = { uploadedFile }
256- mainContent = { ref . current }
257- settings = { { csrfToken, baseUrl, selectFile, dismissAndClose, labels : structure . labels } }
258- /> : < >
259- < MenuBar
260- refreshFilesList = { refreshFilesList }
261- setSearchQuery = { setSearchQuery }
262- openUploader = { ( ) => uploaderRef . current . openUploader ( ) }
263- labels = { structure . labels }
264- />
265- < div className = "browser-body" >
266- < nav className = "folder-structure" >
267- < ul role = "navigation" >
268- { structure . root_folder && < FolderStructure
269- baseUrl = { baseUrl }
270- folder = { structure . root_folder }
271- lastFolderId = { structure . last_folder }
272- setCurrentFolder = { setCurrentFolder }
273- toggleRecursive = { toggleRecursive }
274- refreshStructure = { refreshStructure }
275- isListed = { structure . recursive ? false : null }
276- /> }
277- </ ul >
278- </ nav >
279- < FileUploader
280- folderId = { structure . last_folder }
281- handleUpload = { handleUpload }
282- ref = { uploaderRef }
283- settings = { { csrf_token : csrfToken , base_url : baseUrl } }
284- > {
285- structure . files === null ?
286- < div className = "status" > { gettext ( "Loading files…" ) } </ div > :
287- < FilesList
288- structure = { structure }
289- fetchFiles = { fetchFiles }
290- selectFile = { selectFile }
291- />
292- } </ FileUploader >
293- </ div >
294- </ > }
262+ < BrowserEditor
263+ uploadedFile = { uploadedFile }
264+ mainContent = { ref . current }
265+ settings = { { csrfToken, baseUrl, selectFile, dismissAndClose, labels : structure . labels } }
266+ /> : < >
267+ < MenuBar
268+ ref = { menuBarRef }
269+ openUploader = { ( ) => uploaderRef . current . openUploader ( ) }
270+ labels = { structure . labels }
271+ refreshFilesList = { refreshFilesList }
272+ setDirty = { setDirty }
273+ setSearchQuery = { setSearchQuery }
274+ searchRealm = { searchRealm }
275+ setSearchRealm = { changeSearchRealm }
276+ />
277+ < div className = "browser-body" >
278+ < nav className = "folder-structure" >
279+ < ul role = "navigation" >
280+ { structure . root_folder && < FolderStructure
281+ baseUrl = { baseUrl }
282+ folder = { structure . root_folder }
283+ lastFolderId = { structure . last_folder }
284+ setCurrentFolder = { setCurrentFolder }
285+ toggleRecursive = { toggleRecursive }
286+ refreshStructure = { ( ) => setStructure ( { ...structure } ) }
287+ isListed = { structure . recursive ? false : null }
288+ setCurrentFolderElement = { setCurrentFolderElement }
289+ /> }
290+ </ ul >
291+ </ nav >
292+ < FileUploader
293+ folderId = { structure . last_folder }
294+ handleUpload = { handleUpload }
295+ ref = { uploaderRef }
296+ settings = { { csrf_token : csrfToken , base_url : baseUrl } }
297+ > {
298+ structure . files === null ?
299+ < div className = "status" > { gettext ( "Loading files…" ) } </ div > :
300+ < FilesList
301+ structure = { structure }
302+ setDirty = { setDirty }
303+ selectFile = { selectFile }
304+ />
305+ } </ FileUploader >
306+ </ div >
307+ </ > }
295308 </ div >
296309 < div
297310 className = "close-button"
0 commit comments