mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
Creating FilePreview for FileBrowser.
This commit is contained in:
parent
69e673e0b8
commit
a2e1cfb0eb
4 changed files with 130 additions and 56 deletions
|
|
@ -520,6 +520,7 @@ export async function refreshDirectoryFiles(directory) {
|
|||
}
|
||||
|
||||
export async function getFiles(directories) {
|
||||
console.debug(`getFiles ${directories}`);
|
||||
let body = {directories: directories || []};
|
||||
let response = await apiPost(`${API_URI}/files`, body);
|
||||
let {files} = await response.json();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import _ from 'lodash';
|
|||
import {SortableTable} from "./SortableTable";
|
||||
import {useBrowseFiles} from "../hooks/customHooks";
|
||||
import {DirectoryRefreshButton, FilesRefreshButton} from "./Files";
|
||||
import {useFilePreview} from "./FilePreview";
|
||||
|
||||
function depthIndentation(path) {
|
||||
// Repeated spaces for every folder a path is in.
|
||||
|
|
@ -36,13 +37,13 @@ function Folder({folder, onFolderClick, sortData, selectedPath, onFileClick, onS
|
|||
<TableCell collapsing>
|
||||
<Checkbox checked={selectedPath === folder['path']} onChange={() => onSelect(folder['path'])}/>
|
||||
</TableCell>
|
||||
<TableCell onClick={() => onFolderClick(path)} className='file-path' colSpan={2}>
|
||||
<TableCell onClick={() => onFolderClick(path)} className='file-path' colSpan={2} disabled={is_empty}>
|
||||
{depthIndentation(pathWithNoTrailingSlash)}
|
||||
{is_empty ? <Icon name='folder outline'/> : <Icon name='folder'/>}
|
||||
{name}
|
||||
</TableCell>
|
||||
</TableRow>;
|
||||
if (children && !_.isEmpty(children)) {
|
||||
if (children) {
|
||||
// Folder has children, recursively display them.
|
||||
children = sortData(children);
|
||||
let childPaths = [];
|
||||
|
|
@ -112,51 +113,7 @@ export function FileBrowser() {
|
|||
key: 'path', text: 'Path', sortBy: i => i['path'].toLowerCase()
|
||||
}, {key: 'size', text: 'Size', sortBy: 'size'},];
|
||||
|
||||
const [modal, setModal] = React.useState(null);
|
||||
const onFileClick = (path) => {
|
||||
const {mimetype} = path;
|
||||
const url = `/media/${path['path']}`;
|
||||
let modalBody;
|
||||
if (mimetype === 'text/plain' || mimetype === 'text/html') {
|
||||
modalBody = <ModalContent>
|
||||
<div className='full-height'>
|
||||
<iframe title='textModal' src={url}
|
||||
style={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
border: 'none',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
}}/>
|
||||
</div>
|
||||
</ModalContent>;
|
||||
} else if (mimetype.startsWith('image/')) {
|
||||
modalBody = <ModalContent>
|
||||
<a href={url}>
|
||||
<Image src={url}/>
|
||||
</a>
|
||||
</ModalContent>;
|
||||
}
|
||||
|
||||
if (modalBody) {
|
||||
// Create modal that will instantly display the file.
|
||||
setModal(<Modal closeIcon
|
||||
size='fullscreen'
|
||||
open={true}
|
||||
onClose={() => setModal(null)}
|
||||
>
|
||||
{modalBody}
|
||||
<ModalActions>
|
||||
<SButton color='blue' onClick={() => window.open(url)}>Open</SButton>
|
||||
<SButton onClick={() => setModal(null)}>Close</SButton>
|
||||
</ModalActions>
|
||||
</Modal>)
|
||||
} else {
|
||||
// No special handler for this file type, just open it.
|
||||
setModal(null);
|
||||
window.open(url);
|
||||
}
|
||||
}
|
||||
const {modal, setPath} = useFilePreview();
|
||||
|
||||
const onSelect = (path) => {
|
||||
if (path.endsWith('/') && selectedPath === path) {
|
||||
|
|
@ -241,7 +198,7 @@ export function FileBrowser() {
|
|||
key={i['key']}
|
||||
path={i}
|
||||
onFolderClick={onFolderClick}
|
||||
onFileClick={onFileClick}
|
||||
onFileClick={(i) => setPath(i)}
|
||||
sortData={sortData}
|
||||
selectedPath={selectedPath}
|
||||
onSelect={onSelect}
|
||||
|
|
|
|||
112
app/src/components/FilePreview.js
Normal file
112
app/src/components/FilePreview.js
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import React, {useEffect} from "react";
|
||||
import _ from "lodash";
|
||||
import {Button as SButton, Image, Modal, ModalActions, ModalContent} from "semantic-ui-react";
|
||||
|
||||
function getMediaPathURL(path) {
|
||||
return `/media/${encodeURIComponent(path['path'])}`;
|
||||
}
|
||||
|
||||
function getDownloadPathURL(path) {
|
||||
return `/download/${encodeURIComponent(path['path'])}`;
|
||||
}
|
||||
|
||||
function getEpubViewerURL(path) {
|
||||
return `/epub.html?url=${getDownloadPathURL(path)}`;
|
||||
}
|
||||
|
||||
function getIframeModal(path) {
|
||||
const url = getMediaPathURL(path);
|
||||
return <ModalContent>
|
||||
<div className='full-height'>
|
||||
<iframe title='textModal' src={url}
|
||||
style={{
|
||||
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
|
||||
}}/>
|
||||
</div>
|
||||
</ModalContent>
|
||||
}
|
||||
|
||||
function getImageModal(path) {
|
||||
const url = getMediaPathURL(path);
|
||||
return <ModalContent>
|
||||
<a href={url}>
|
||||
<Image src={url}/>
|
||||
</a>
|
||||
</ModalContent>
|
||||
}
|
||||
|
||||
function getEpubModal(path) {
|
||||
const downloadURL = getDownloadPathURL(path);
|
||||
const viewerUrl = `/epub.html?url=${downloadURL}`;
|
||||
return <ModalContent>
|
||||
<div className='full-height'>
|
||||
<iframe title='textModal' src={viewerUrl}
|
||||
style={{
|
||||
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
|
||||
}}/>
|
||||
</div>
|
||||
</ModalContent>
|
||||
}
|
||||
|
||||
function getVideoModal(path) {
|
||||
const url = getMediaPathURL(path);
|
||||
return <ModalContent>
|
||||
<video controls
|
||||
autoPlay={true}
|
||||
id="player"
|
||||
playsInline={true}
|
||||
style={{maxWidth: '100%'}}
|
||||
>
|
||||
<source src={url} type="video/mp4"/>
|
||||
</video>
|
||||
</ModalContent>
|
||||
}
|
||||
|
||||
export function useFilePreview() {
|
||||
const [path, setPath] = React.useState(null);
|
||||
const [modal, setModal] = React.useState(null);
|
||||
|
||||
function setModalContent(content, url, downloadURL) {
|
||||
setModal(<Modal closeIcon
|
||||
size='fullscreen'
|
||||
open={true}
|
||||
onClose={() => setModal(null)}
|
||||
>
|
||||
{content}
|
||||
<ModalActions>
|
||||
{downloadURL &&
|
||||
<SButton color='yellow' onClick={() => window.open(downloadURL)} floated='left'>Download</SButton>}
|
||||
<SButton color='blue' onClick={() => window.open(url)}>Open</SButton>
|
||||
<SButton onClick={() => setPath(null)}>Close</SButton>
|
||||
</ModalActions>
|
||||
</Modal>);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setModal(null);
|
||||
if (path && !_.isEmpty(path)) {
|
||||
const {mimetype, size} = path;
|
||||
console.debug(`useFilePreview path=${path['path']} mimetype=${mimetype}`);
|
||||
const url = getMediaPathURL(path);
|
||||
const downloadURL = getDownloadPathURL(path);
|
||||
if (mimetype.startsWith('text/') && size >= 10000) {
|
||||
// Large text files should be downloaded.
|
||||
window.open(downloadURL);
|
||||
} else if (mimetype.startsWith('text/') || mimetype.startsWith('application/json')) {
|
||||
setModalContent(getIframeModal(path), url, downloadURL);
|
||||
} else if (mimetype.startsWith('video/')) {
|
||||
setModalContent(getVideoModal(path), url, downloadURL);
|
||||
} else if (mimetype.startsWith('application/epub')) {
|
||||
const viewerURL = getEpubViewerURL(path);
|
||||
setModalContent(getEpubModal(path), viewerURL, downloadURL);
|
||||
} else if (mimetype.startsWith('image/')) {
|
||||
setModalContent(getImageModal(path), url);
|
||||
} else {
|
||||
// No special handler for this file type, just open it.
|
||||
window.open(downloadURL);
|
||||
}
|
||||
}
|
||||
}, [path]);
|
||||
|
||||
return {modal, setPath};
|
||||
}
|
||||
|
|
@ -37,12 +37,13 @@ except ImportError:
|
|||
logger = logger.getChild(__name__)
|
||||
|
||||
__all__ = ['list_directories_contents', 'delete_file', 'split_path_stem_and_suffix', 'refresh_files', 'search_files',
|
||||
'get_mimetype',
|
||||
'split_file_name_words']
|
||||
'get_mimetype', 'split_file_name_words']
|
||||
|
||||
|
||||
@cachetools.func.ttl_cache(10_000, 30.0)
|
||||
def _get_file_dict(file: pathlib.Path) -> Dict:
|
||||
def _get_file_dict(file: pathlib.Path,
|
||||
directories_cache: str, # Used to cache by requested directories.
|
||||
) -> Dict:
|
||||
media_directory = get_media_directory()
|
||||
return dict(
|
||||
path=file.relative_to(media_directory),
|
||||
|
|
@ -52,7 +53,9 @@ def _get_file_dict(file: pathlib.Path) -> Dict:
|
|||
|
||||
|
||||
@cachetools.func.ttl_cache(10_000, 30.0)
|
||||
def _get_directory_dict(directory: pathlib.Path) -> Dict:
|
||||
def _get_directory_dict(directory: pathlib.Path,
|
||||
directories_cache: str, # Used to cache by requested directories.
|
||||
) -> Dict:
|
||||
media_directory = get_media_directory()
|
||||
return dict(
|
||||
path=f'{directory.relative_to(media_directory)}/',
|
||||
|
|
@ -61,16 +64,17 @@ def _get_directory_dict(directory: pathlib.Path) -> Dict:
|
|||
|
||||
|
||||
def _get_recursive_directory_dict(directory: pathlib.Path, directories: List[pathlib.Path]) -> Dict:
|
||||
d = _get_directory_dict(directory)
|
||||
directories_cache = str(sorted(directories))
|
||||
d = _get_directory_dict(directory, directories_cache)
|
||||
if directory in directories:
|
||||
children = dict()
|
||||
for path in directory.iterdir():
|
||||
if path.is_dir() and path in directories:
|
||||
children[f'{path.name}/'] = _get_recursive_directory_dict(path, directories)
|
||||
elif path.is_dir():
|
||||
children[f'{path.name}/'] = _get_directory_dict(path)
|
||||
children[f'{path.name}/'] = _get_directory_dict(path, directories_cache)
|
||||
else:
|
||||
children[path.name] = _get_file_dict(path)
|
||||
children[path.name] = _get_file_dict(path, directories_cache)
|
||||
d['children'] = children
|
||||
return d
|
||||
|
||||
|
|
@ -101,7 +105,7 @@ def list_directories_contents(directories_: List[str]) -> Dict:
|
|||
if path.is_dir():
|
||||
paths[f'{path.name}/'] = _get_recursive_directory_dict(path, directories)
|
||||
else:
|
||||
paths[path.name] = _get_file_dict(path)
|
||||
paths[path.name] = _get_file_dict(path, str(directories))
|
||||
|
||||
return paths
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue