cnap-cobre/synapse

View on GitHub
frontend/src/components/FileBrowser/FileBrowser.js

Summary

Maintainability
A
2 hrs
Test Coverage
// @flow
 
import React from 'react';
import { connect } from 'react-redux';
import Dropzone from 'react-dropzone';
import { Link, push } from 'redux-json-router';
import pathUtil from 'path';
import FileBreadcrumbs from '../FileBreadcrumbs/FileBreadcrumbs';
import FileBrowserControls from '../FileBrowserControls/FileBrowserControls';
import FileBrowserGrid from '../FileBrowserGrid/FileBrowserGrid';
import FileBrowserList from '../FileBrowserList/FileBrowserList';
import Loader from '../Loader/Loader';
 
import { setBrowserPath } from '../../store/ui/browserPaths/BrowserPaths';
 
import {
addFocusedFile, removeFocusedFile, setFocusedFile, setFocusedFilesList,
} from '../../store/ui/focusedFiles/FocusedFiles';
import { getFileViewFormat, getFocusedFilePaths } from '../../store/ui/reducer';
import { fileActions, fileListActions } from '../../store/files/Files';
import type { FileSystemType } from '../../types/fileSystemTypes';
import type { FileType } from '../../types/fileTypes';
 
type Props = {
system: FileSystemType,
systemPrefix: string,
path: string,
pathname: string,
showDotfiles: boolean,
toggleDotfiles(): typeof undefined,
loading: boolean,
error: boolean,
list: Array<FileType>,
fileViewFormat: boolean,
focusedFilePaths: Array<string>,
uploadFile(File, string): typeof undefined,
fetchFileList(string): typeof undefined,
navigateToNewPath(string, string): typeof undefined,
browserHistoryNavPush(string): typeof undefined,
addFocusedFile(string): typeof undefined,
setFocusedFile(string): typeof undefined,
removeFocusedFile(string): typeof undefined,
}
 
class FileBrowser extends React.Component<Props> {
shouldComponentUpdate(nextProps) {
// No point in rendering if the tab isn't being shown.
return nextProps.pathname.indexOf(
nextProps.systemPrefix,
) === 0;
}
 
handleRefresh = path => () => {
const { fetchFileList } = this.props;
fetchFileList(path);
};
 
handleContextMenu = (file) => {
const { focusedFilePaths, setFocusedFile } = this.props;
console.log("HELLO WORLD", file.fullPath);
console.log('FOCUSED', focusedFilePaths);
if (focusedFilePaths.indexOf(file.fullPath) === -1) {
console.log('ASDFASDFASDF')
setFocusedFile(file.fullPath);
}
};
 
handleDoubleClick = (file) => {
const {
system, path, navigateToNewPath, browserHistoryNavPush,
} = this.props;
 
if (file.type === 'dir') {
browserHistoryNavPush([
'.',
file.name,
'',
].join('/'));
 
navigateToNewPath(
`${system.provider}.${system.id}`,
`${pathUtil.resolve(path, file.name).slice(0)}/`,
);
}
};
 
handleSingleClick = (file, list, e) => {
const {
focusedFilePaths, removeFocusedFile, addFocusedFile, setFocusedFile,
} = this.props;
 
e.preventDefault();
const selected = focusedFilePaths;
 
if (e.ctrlKey) {
// If we are already selected, remove from selection
// Else, add to selection
if (selected.indexOf(file.fullPath) !== -1) {
return removeFocusedFile(file.fullPath);
}
return addFocusedFile(file.fullPath);
}
 
if (e.shiftKey && selected.length === 0) {
// Revert to single click behavior
e.ctrlKey = true;
return this.handleSingleClick(file, list, e);
}
 
if (e.shiftKey && selected.length === 1 && selected[0] === file.fullPath) {
// If we shift + click on the only selected file, do nothing.
return null;
}
 
if (e.shiftKey) {
const mostRecentSelection = selected.slice(-1)[0];
const mostRecentSelectionIndex = list.findIndex(f => f.fullPath === mostRecentSelection);
const currentSelectionIndex = list.findIndex(f => f.fullPath === file.fullPath);
 
Avoid too many `return` statements within this function.
return setFocusedFilesList(
list.map(f => f.fullPath).slice(
Math.min(mostRecentSelectionIndex, currentSelectionIndex),
Math.max(mostRecentSelectionIndex, currentSelectionIndex) + 1,
),
);
}
 
Avoid too many `return` statements within this function.
return setFocusedFile(file.fullPath);
};
 
handleFileDropzone = (files) => {
const { path, uploadFile } = this.props;
 
// eslint-disable-next-line
for (let i = 0; i < files.length; i++) {
uploadFile(files[i], path);
}
};
 
Function `render` has 49 lines of code (exceeds 25 allowed). Consider refactoring.
render() {
const {
system,
systemPrefix,
pathname,
path,
showDotfiles,
toggleDotfiles,
fileViewFormat,
loading,
error,
list,
focusedFilePaths,
} = this.props;
 
const FileViewComponent = (fileViewFormat ? FileBrowserGrid : FileBrowserList);
 
return (
<div className="card-content table-responsive table-full-width">
<Dropzone
style={{}}
onDrop={this.handleFileDropzone}
disableClick
>
<FileBreadcrumbs
systemName={system.name}
prefix={systemPrefix}
pathname={pathname}
crumbComponent={Link}
/>
 
<FileBrowserControls
id={system.id}
handleRefresh={this.handleRefresh(path)}
showDotfiles={showDotfiles}
toggleDotfiles={toggleDotfiles}
path={path}
/>
 
<FileViewComponent
showDotfiles={showDotfiles}
path={path}
handleContextMenu={this.handleContextMenu}
handleDoubleClick={this.handleDoubleClick}
handleSingleClick={this.handleSingleClick}
loading={loading}
error={error}
list={list}
focusedFilePaths={focusedFilePaths}
/>
 
<Loader visible={loading} />
</Dropzone>
</div>
);
}
}
 
const mapStateToProps = (store, ownProps) => {
const filesAtPath = store.files[ownProps.path];
 
const loading = (filesAtPath === undefined || filesAtPath.loading);
const list = (loading) ? [] : filesAtPath.files;
 
return {
loading,
TODO found
error: false, // TODO: fix this hack and actually handle errors
list: list || [],
fileViewFormat: getFileViewFormat(store),
focusedFilePaths: getFocusedFilePaths(store),
};
};
 
const mapDispatchToProps = {
fetchFileList: fileListActions.pending,
setFocusedFile,
browserHistoryNavPush: push,
navigateToNewPath: setBrowserPath,
removeFocusedFile,
addFocusedFile,
setFocusedFilesList,
uploadFile: fileActions.uploadFile,
};
 
export default connect(
mapStateToProps,
mapDispatchToProps,
)(FileBrowser);