|
1 | | -import React, { useEffect, useState } from 'react'; |
2 | | -import FlashMessageRender from '@/components/FlashMessageRender'; |
3 | | -import { ServerContext } from '@/state/server'; |
4 | | -import { Actions, useStoreActions } from 'easy-peasy'; |
5 | | -import { ApplicationStore } from '@/state'; |
| 1 | +import React, { useEffect } from 'react'; |
6 | 2 | import { httpErrorToHuman } from '@/api/http'; |
7 | 3 | import { CSSTransition } from 'react-transition-group'; |
8 | 4 | import Spinner from '@/components/elements/Spinner'; |
9 | 5 | import FileObjectRow from '@/components/server/files/FileObjectRow'; |
10 | 6 | import FileManagerBreadcrumbs from '@/components/server/files/FileManagerBreadcrumbs'; |
11 | | -import { FileObject } from '@/api/server/files/loadDirectory'; |
| 7 | +import loadDirectory, { FileObject } from '@/api/server/files/loadDirectory'; |
12 | 8 | import NewDirectoryButton from '@/components/server/files/NewDirectoryButton'; |
13 | | -import { Link } from 'react-router-dom'; |
| 9 | +import { Link, useLocation } from 'react-router-dom'; |
14 | 10 | import Can from '@/components/elements/Can'; |
15 | 11 | import PageContentBlock from '@/components/elements/PageContentBlock'; |
16 | 12 | import ServerError from '@/components/screens/ServerError'; |
17 | 13 | import tw from 'twin.macro'; |
18 | 14 | import Button from '@/components/elements/Button'; |
| 15 | +import useSWR from 'swr'; |
| 16 | +import useServer from '@/plugins/useServer'; |
| 17 | +import { cleanDirectoryPath } from '@/helpers'; |
| 18 | +import { ServerContext } from '@/state/server'; |
19 | 19 |
|
20 | 20 | const sortFiles = (files: FileObject[]): FileObject[] => { |
21 | 21 | return files.sort((a, b) => a.name.localeCompare(b.name)) |
22 | 22 | .sort((a, b) => a.isFile === b.isFile ? 0 : (a.isFile ? 1 : -1)); |
23 | 23 | }; |
24 | 24 |
|
25 | 25 | export default () => { |
26 | | - const [ error, setError ] = useState(''); |
27 | | - const [ loading, setLoading ] = useState(true); |
28 | | - const { clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes); |
29 | | - const { id } = ServerContext.useStoreState(state => state.server.data!); |
30 | | - const { contents: files } = ServerContext.useStoreState(state => state.files); |
31 | | - const { getDirectoryContents } = ServerContext.useStoreActions(actions => actions.files); |
| 26 | + const { hash } = useLocation(); |
| 27 | + const { id, uuid } = useServer(); |
| 28 | + const setDirectory = ServerContext.useStoreActions(actions => actions.files.setDirectory); |
32 | 29 |
|
33 | | - const loadContents = () => { |
34 | | - setError(''); |
35 | | - clearFlashes(); |
36 | | - setLoading(true); |
37 | | - getDirectoryContents(window.location.hash) |
38 | | - .then(() => setLoading(false)) |
39 | | - .catch(error => { |
40 | | - console.error(error.message, { error }); |
41 | | - setError(httpErrorToHuman(error)); |
42 | | - }); |
43 | | - }; |
| 30 | + const { data: files, error, mutate } = useSWR( |
| 31 | + `${uuid}:files:${hash}`, |
| 32 | + () => loadDirectory(uuid, cleanDirectoryPath(window.location.hash)), |
| 33 | + ); |
44 | 34 |
|
45 | 35 | useEffect(() => { |
46 | | - loadContents(); |
47 | | - }, []); |
| 36 | + setDirectory(hash.length > 0 ? hash : '/'); |
| 37 | + }, [ hash ]); |
48 | 38 |
|
49 | 39 | if (error) { |
50 | 40 | return ( |
51 | | - <ServerError |
52 | | - message={error} |
53 | | - onRetry={() => loadContents()} |
54 | | - /> |
| 41 | + <ServerError message={httpErrorToHuman(error)} onRetry={() => mutate()}/> |
55 | 42 | ); |
56 | 43 | } |
57 | 44 |
|
58 | 45 | return ( |
59 | | - <PageContentBlock> |
60 | | - <FlashMessageRender byKey={'files'} css={tw`mb-4`}/> |
61 | | - <React.Fragment> |
62 | | - <FileManagerBreadcrumbs/> |
63 | | - { |
64 | | - loading ? |
65 | | - <Spinner size={'large'} centered/> |
66 | | - : |
67 | | - <React.Fragment> |
68 | | - {!files.length ? |
69 | | - <p css={tw`text-sm text-neutral-400 text-center`}> |
70 | | - This directory seems to be empty. |
71 | | - </p> |
72 | | - : |
73 | | - <CSSTransition classNames={'fade'} timeout={150} appear in> |
74 | | - <React.Fragment> |
75 | | - <div> |
76 | | - {files.length > 250 ? |
77 | | - <React.Fragment> |
78 | | - <div css={tw`rounded bg-yellow-400 mb-px p-3`}> |
79 | | - <p css={tw`text-yellow-900 text-sm text-center`}> |
80 | | - This directory is too large to display in the browser, |
81 | | - limiting the output to the first 250 files. |
82 | | - </p> |
83 | | - </div> |
84 | | - { |
85 | | - sortFiles(files.slice(0, 250)).map(file => ( |
86 | | - <FileObjectRow key={file.uuid} file={file}/> |
87 | | - )) |
88 | | - } |
89 | | - </React.Fragment> |
90 | | - : |
91 | | - sortFiles(files).map(file => ( |
92 | | - <FileObjectRow key={file.uuid} file={file}/> |
93 | | - )) |
94 | | - } |
| 46 | + <PageContentBlock showFlashKey={'files'}> |
| 47 | + <FileManagerBreadcrumbs/> |
| 48 | + { |
| 49 | + !files ? |
| 50 | + <Spinner size={'large'} centered/> |
| 51 | + : |
| 52 | + <> |
| 53 | + {!files.length ? |
| 54 | + <p css={tw`text-sm text-neutral-400 text-center`}> |
| 55 | + This directory seems to be empty. |
| 56 | + </p> |
| 57 | + : |
| 58 | + <CSSTransition classNames={'fade'} timeout={150} appear in> |
| 59 | + <React.Fragment> |
| 60 | + <div> |
| 61 | + {files.length > 250 && |
| 62 | + <div css={tw`rounded bg-yellow-400 mb-px p-3`}> |
| 63 | + <p css={tw`text-yellow-900 text-sm text-center`}> |
| 64 | + This directory is too large to display in the browser, |
| 65 | + limiting the output to the first 250 files. |
| 66 | + </p> |
95 | 67 | </div> |
96 | | - </React.Fragment> |
97 | | - </CSSTransition> |
98 | | - } |
99 | | - <Can action={'file.create'}> |
100 | | - <div css={tw`flex justify-end mt-8`}> |
101 | | - <NewDirectoryButton/> |
102 | | - <Button |
103 | | - // @ts-ignore |
104 | | - as={Link} |
105 | | - to={`/server/${id}/files/new${window.location.hash}`} |
106 | | - > |
107 | | - New File |
108 | | - </Button> |
109 | | - </div> |
110 | | - </Can> |
111 | | - </React.Fragment> |
112 | | - } |
113 | | - </React.Fragment> |
| 68 | + } |
| 69 | + { |
| 70 | + sortFiles(files.slice(0, 250)).map(file => ( |
| 71 | + <FileObjectRow key={file.uuid} file={file}/> |
| 72 | + )) |
| 73 | + } |
| 74 | + </div> |
| 75 | + </React.Fragment> |
| 76 | + </CSSTransition> |
| 77 | + } |
| 78 | + <Can action={'file.create'}> |
| 79 | + <div css={tw`flex justify-end mt-8`}> |
| 80 | + <NewDirectoryButton/> |
| 81 | + <Button |
| 82 | + // @ts-ignore |
| 83 | + as={Link} |
| 84 | + to={`/server/${id}/files/new${window.location.hash}`} |
| 85 | + > |
| 86 | + New File |
| 87 | + </Button> |
| 88 | + </div> |
| 89 | + </Can> |
| 90 | + </> |
| 91 | + } |
114 | 92 | </PageContentBlock> |
115 | 93 | ); |
116 | 94 | }; |
0 commit comments