-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
task/WG-418 - File Listing Component Enhancement and Fixes #337
Changes from all commits
5473be9
f317eba
332bf11
44fa293
5c9dba6
62e4d8d
b2b5e45
2b2a9ca
5e965d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,8 +23,10 @@ | |
} from '@hazmapper/hooks'; | ||
import { File, TTapisSystem } from '@hazmapper/types'; | ||
import { serializeToChonkyFile } from '@hazmapper/utils/fileUtils'; | ||
import { debounce } from 'lodash'; | ||
|
||
const DEFAULT_NO_FILE_EXTENSIONS: string[] = []; | ||
const DEFAULT_FILE_LIMIT = 500; | ||
|
||
const _FileBrowser = FileBrowser as React.MemoExoticComponent< | ||
React.ForwardRefExoticComponent< | ||
|
@@ -65,20 +67,28 @@ | |
|
||
const { data: user } = useAuthenticatedUser(); | ||
|
||
const [chonkyFiles, setChonkyFiles] = React.useState<any>([]); | ||
const [chonkyFiles, setChonkyFiles] = React.useState<any>( | ||
new Array(8).fill(null) | ||
); | ||
const [folderChain, setFolderChain] = React.useState<any>([]); | ||
const [path, setPath] = React.useState<string>(''); | ||
const [selectedSystem, setSelectedSystem] = React.useState<TTapisSystem>( | ||
myDataSystem as TTapisSystem | ||
); | ||
const [hasError, setHasError] = React.useState(false); | ||
const [loadingMoreFiles, setLoadingMoreFiles] = React.useState(false); | ||
const [hasMoreFiles, setHasMoreFiles] = React.useState(false); | ||
const [listingState, setListingState] = React.useState({ | ||
path: '', | ||
offset: 0, | ||
}); | ||
|
||
const { data: files, refetch } = useFiles({ | ||
system: selectedSystem?.id || '', | ||
path: | ||
path || (selectedSystem?.id === myDataSystem?.id ? user?.username : ''), | ||
offset: '0', | ||
limit: '100' /* TODO https://tacc-main.atlassian.net/browse/WG-418 */, | ||
listingState.path || | ||
(selectedSystem?.id === myDataSystem?.id ? user?.username : ''), | ||
offset: listingState.offset.toString(), | ||
limit: DEFAULT_FILE_LIMIT.toString(), | ||
Comment on lines
+88
to
+91
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Few suggestions for future improvement:
|
||
enabled: !!selectedSystem?.id, | ||
}); | ||
|
||
|
@@ -87,33 +97,98 @@ | |
|
||
const selectedSystemId = watch('system'); | ||
|
||
const setRootFolderChain = (rootPath, sys = selectedSystem) => { | ||
let rootFolderName: string; | ||
|
||
if (sys?.id === myDataSystem?.id) { | ||
rootFolderName = 'My Data'; | ||
} else if (sys?.id === communityDataSystem?.id) { | ||
rootFolderName = 'Community Data'; | ||
} else if (sys?.id === publishedDataSystem?.id) { | ||
rootFolderName = 'Published Data'; | ||
} else { | ||
rootFolderName = 'Project'; | ||
} | ||
|
||
setFolderChain([{ id: rootPath, name: rootFolderName, isDir: true }]); | ||
}; | ||
|
||
useEffect(() => { | ||
if (selectedSystem?.id) { | ||
refetch(); | ||
} | ||
}, [selectedSystem, path, refetch]); | ||
}, [selectedSystem, listingState, refetch]); | ||
|
||
useEffect(() => { | ||
setChonkyFiles( | ||
files?.map((file) => | ||
serializeToChonkyFile(file, allowedFileExtensions) | ||
) || [] | ||
); | ||
if (files) { | ||
setLoadingMoreFiles(false); | ||
const fileListWrapper = document.querySelector('.chonky-fileListWrapper'); | ||
|
||
if (!fileListWrapper) return; | ||
|
||
const dynamicListContainer = fileListWrapper.querySelector( | ||
'[class^="listContainer-"]' | ||
); | ||
|
||
if (!dynamicListContainer) return; | ||
|
||
const handleScroll = debounce(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick and you don't need to take my advice, but personally I like to avoid lodash. Is there a way to handle the scroll event in this way without the debounce frequency? |
||
if (!hasMoreFiles) return; | ||
const { scrollTop, scrollHeight, clientHeight } = dynamicListContainer; | ||
if (scrollTop + clientHeight >= scrollHeight - 20) { | ||
setLoadingMoreFiles(true); | ||
setListingState((prev) => ({ | ||
...prev, | ||
offset: prev.offset + DEFAULT_FILE_LIMIT, | ||
})); | ||
} | ||
}, 250); | ||
|
||
dynamicListContainer.addEventListener('scroll', handleScroll); | ||
return () => | ||
dynamicListContainer.removeEventListener('scroll', handleScroll); | ||
} | ||
}, [files, hasMoreFiles]); | ||
|
||
useEffect(() => { | ||
if (files) { | ||
if (files.length < DEFAULT_FILE_LIMIT || files.length === 0) { | ||
setHasMoreFiles(false); | ||
} else { | ||
setHasMoreFiles(true); | ||
} | ||
|
||
setChonkyFiles((prevFiles) => { | ||
const filteredPrevFiles = prevFiles.filter(Boolean); // Filter out nulls | ||
const newChonkyFiles = files.map((file) => | ||
serializeToChonkyFile(file, allowedFileExtensions) | ||
); | ||
|
||
if ( | ||
JSON.stringify(prevFiles) === | ||
JSON.stringify([...prevFiles, ...newChonkyFiles]) | ||
) { | ||
setHasMoreFiles(false); | ||
return prevFiles; | ||
} | ||
|
||
return listingState.offset === 0 | ||
? newChonkyFiles | ||
: [...filteredPrevFiles, ...newChonkyFiles]; | ||
}); | ||
} | ||
if (!folderChain.length && user?.username) { | ||
setFolderChain([{ id: user.username, name: user.username, isDir: true }]); | ||
setRootFolderChain(user.username); | ||
} | ||
}, [ | ||
files, | ||
folderChain.length, | ||
user?.username, | ||
allowedFileExtensions, | ||
hasError, | ||
]); | ||
}, [files, folderChain.length, user?.username, allowedFileExtensions]); | ||
Check warning on line 183 in react/src/components/Files/FileListing.tsx
|
||
|
||
const FileListingIcon = (props: any) => <Icon name={props.icon} />; | ||
|
||
useEffect(() => { | ||
if (!selectedSystemId || selectedSystem?.id === selectedSystemId) { | ||
return; | ||
} | ||
|
||
setHasError(false); | ||
setChonkyFiles(new Array(8).fill(null)); | ||
setFolderChain([null]); | ||
|
@@ -127,24 +202,16 @@ | |
|
||
setSelectedSystem(sys); | ||
const rootFolder = sys.id === myDataSystem?.id ? user?.username : '/'; | ||
setPath(rootFolder); | ||
|
||
setListingState({ | ||
path: rootFolder, | ||
offset: 0, | ||
}); | ||
|
||
onSystemSelect?.(sys.id); | ||
onFolderSelect?.(rootFolder); | ||
let rootFolderName: string; | ||
|
||
if (sys.id === myDataSystem?.id) { | ||
rootFolderName = 'My Data'; | ||
} else if (sys.id === communityDataSystem?.id) { | ||
rootFolderName = 'Community Data'; | ||
} else if (sys.id === publishedDataSystem?.id) { | ||
rootFolderName = 'Published Data'; | ||
} else { | ||
rootFolderName = 'Project'; | ||
} | ||
|
||
setFolderChain([{ id: rootFolder, name: rootFolderName, isDir: true }]); | ||
setRootFolderChain(rootFolder, sys); | ||
}, [selectedSystemId]); | ||
Check warning on line 214 in react/src/components/Files/FileListing.tsx
|
||
|
||
const handleFileAction = useCallback( | ||
(data: ChonkyFileActionData) => { | ||
|
@@ -152,14 +219,18 @@ | |
const file: FileData = data.payload.targetFile as FileData; | ||
|
||
if (file.isDir) { | ||
setPath(file.id); | ||
setListingState({ | ||
path: file.id, | ||
offset: 0, | ||
}); | ||
setFolderChain((prev) => { | ||
const index = prev.findIndex((folder) => folder.id === file.id); | ||
return index === -1 ? [...prev, file] : prev.slice(0, index + 1); | ||
}); | ||
|
||
onFolderSelect?.(file.id); | ||
} else { | ||
if (!file.selectable) return; | ||
const selectedFile = files?.find((f) => f.path === file.id); | ||
if (selectedFile) { | ||
onFileSelect?.([selectedFile]); | ||
|
@@ -178,7 +249,7 @@ | |
(f) => filePaths.includes(f.path) && f.type !== 'dir' | ||
) || []; | ||
|
||
onFileSelect?.(newSelectedFiles); // P | ||
onFileSelect?.(newSelectedFiles); | ||
} | ||
}, | ||
[files, onFileSelect, onFolderSelect] | ||
|
@@ -233,9 +304,13 @@ | |
clearSelectionOnOutsideClick | ||
iconComponent={FileListingIcon} | ||
onFileAction={handleFileAction} | ||
defaultSortActionId={null} | ||
> | ||
<FileNavbar /> | ||
<FileList /> | ||
{loadingMoreFiles && ( | ||
<p style={{ textAlign: 'center' }}>Loading...</p> | ||
)} | ||
Comment on lines
+311
to
+313
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could use a bit more vertical padding here |
||
</_FileBrowser> | ||
)} | ||
</div> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use the
isLoading
state from the useFiles hook instead?