2023-02-15 20:58:07 -07:00
|
|
|
import React from "react";
|
2023-02-15 17:40:57 -07:00
|
|
|
import _ from "lodash";
|
2023-05-03 12:46:57 -06:00
|
|
|
import {Grid, Header as SHeader, Image,} from "semantic-ui-react";
|
2023-05-29 18:04:03 -06:00
|
|
|
import {TagsSelector} from "../Tags";
|
2023-03-29 13:46:39 -06:00
|
|
|
import {Media} from "../contexts/contexts";
|
|
|
|
|
import {encodeMediaPath} from "./Common";
|
2023-05-29 18:04:03 -06:00
|
|
|
import {fetchFile, tagFileGroup, untagFileGroup} from "../api";
|
2023-04-04 16:17:46 -06:00
|
|
|
import {StlViewer} from "react-stl-viewer";
|
2023-04-18 22:03:45 -06:00
|
|
|
import {Button, Modal, ModalActions, ModalContent, ModalHeader} from "./Theme";
|
2023-05-29 18:04:03 -06:00
|
|
|
import {toast} from "react-semantic-toasts-2";
|
2023-02-15 17:40:57 -07:00
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getMediaPathURL(previewFile) {
|
|
|
|
|
if (previewFile['primary_path']) {
|
|
|
|
|
return `/media/${encodeMediaPath(previewFile['primary_path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
} else {
|
2023-03-29 13:46:39 -06:00
|
|
|
return `/media/${encodeMediaPath(previewFile['path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
}
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getDownloadPathURL(previewFile) {
|
|
|
|
|
if (previewFile['primary_path']) {
|
|
|
|
|
return `/download/${encodeMediaPath(previewFile['primary_path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
} else {
|
2023-03-29 13:46:39 -06:00
|
|
|
return `/download/${encodeMediaPath(previewFile['path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
}
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getEpubViewerURL(previewFile) {
|
|
|
|
|
if (previewFile['primary_path']) {
|
2023-05-29 18:04:03 -06:00
|
|
|
return `/epub/epub.html?url=download/${encodeMediaPath(previewFile['primary_path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
} else {
|
2023-05-29 18:04:03 -06:00
|
|
|
return `/epub/epub.html?url=download/${encodeMediaPath(previewFile['path'])}`;
|
2023-03-27 11:37:00 -06:00
|
|
|
}
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getIframeModal(previewFile) {
|
|
|
|
|
const url = getMediaPathURL(previewFile);
|
2023-02-15 17:40:57 -07:00
|
|
|
return <ModalContent>
|
|
|
|
|
<div className='full-height'>
|
|
|
|
|
<iframe title='textModal' src={url}
|
|
|
|
|
style={{
|
|
|
|
|
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
|
2023-04-18 22:03:45 -06:00
|
|
|
// Use white to avoid iframe displaying with dark-theme.
|
|
|
|
|
backgroundColor: '#ffffff',
|
2023-02-15 17:40:57 -07:00
|
|
|
}}/>
|
|
|
|
|
</div>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getImageModal(previewFile) {
|
|
|
|
|
const path = previewFile.primary_path ?? previewFile.path;
|
|
|
|
|
const url = getMediaPathURL(previewFile);
|
|
|
|
|
// Get the file name from the absolute path.
|
|
|
|
|
const name = path.replace(/^.*[\\\/]/, '');
|
2023-02-22 11:48:25 -07:00
|
|
|
return <React.Fragment>
|
|
|
|
|
<ModalHeader>
|
2023-02-22 13:02:55 -07:00
|
|
|
{name}
|
2023-02-22 11:48:25 -07:00
|
|
|
</ModalHeader>
|
|
|
|
|
<ModalContent>
|
|
|
|
|
<a href={url}>
|
|
|
|
|
<Image src={url}/>
|
|
|
|
|
</a>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
</React.Fragment>
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getEpubModal(previewFile) {
|
|
|
|
|
const downloadURL = getDownloadPathURL(previewFile);
|
2023-05-29 18:04:03 -06:00
|
|
|
const viewerUrl = `/epub/epub.html?url=${downloadURL}`;
|
2023-02-15 17:40:57 -07:00
|
|
|
return <ModalContent>
|
|
|
|
|
<div className='full-height'>
|
|
|
|
|
<iframe title='textModal' src={viewerUrl}
|
|
|
|
|
style={{
|
|
|
|
|
height: '100%', width: '100%', border: 'none', position: 'absolute', top: 0,
|
|
|
|
|
}}/>
|
|
|
|
|
</div>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getVideoModal(previewFile) {
|
|
|
|
|
const url = getMediaPathURL(previewFile);
|
|
|
|
|
const path = previewFile.primary_path ?? previewFile.path;
|
|
|
|
|
const name = path.replace(/^.*[\\\/]/, '');
|
|
|
|
|
const type = previewFile.mimetype ? previewFile.mimetype : 'video/mp4';
|
2023-02-22 11:48:25 -07:00
|
|
|
return <React.Fragment>
|
|
|
|
|
<ModalHeader>
|
2023-02-22 13:02:55 -07:00
|
|
|
{name}
|
2023-02-22 11:48:25 -07:00
|
|
|
</ModalHeader>
|
|
|
|
|
<ModalContent>
|
|
|
|
|
<SHeader as='h5'>{path['path']}</SHeader>
|
|
|
|
|
<video controls
|
|
|
|
|
autoPlay={true}
|
|
|
|
|
id="player"
|
|
|
|
|
playsInline={true}
|
|
|
|
|
style={{maxWidth: '100%'}}
|
|
|
|
|
>
|
|
|
|
|
<source src={url} type={type}/>
|
|
|
|
|
</video>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-29 13:46:39 -06:00
|
|
|
function getAudioModal(previewFile) {
|
|
|
|
|
const url = getMediaPathURL(previewFile);
|
|
|
|
|
const path = previewFile.primary_path ?? previewFile.path;
|
|
|
|
|
const name = path.replace(/^.*[\\\/]/, '');
|
2023-02-22 17:58:36 -07:00
|
|
|
// Some audio files may not have the correct mimetype. Trust the suffix identifies an audio file.
|
2023-03-29 13:46:39 -06:00
|
|
|
const type = previewFile['mimetype'] !== 'application/octet-stream' ? previewFile['mimetype'] : 'audio/mpeg';
|
2023-02-22 11:48:25 -07:00
|
|
|
return <React.Fragment>
|
|
|
|
|
<ModalHeader>
|
2023-02-22 13:02:55 -07:00
|
|
|
{name}
|
2023-04-04 16:17:46 -06:00
|
|
|
</ModalHeader>
|
|
|
|
|
<ModalContent>
|
|
|
|
|
<audio controls
|
|
|
|
|
autoPlay={true}
|
|
|
|
|
id="player"
|
|
|
|
|
playsInline={true}
|
|
|
|
|
style={{width: '90%', maxWidth: '95%'}}
|
|
|
|
|
>
|
|
|
|
|
<source src={url} type={type}/>
|
|
|
|
|
</audio>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getSTLModal(previewFile) {
|
|
|
|
|
const url = getMediaPathURL(previewFile);
|
|
|
|
|
const path = previewFile.primary_path ?? previewFile.path;
|
|
|
|
|
const name = path.replace(/^.*[\\\/]/, '');
|
|
|
|
|
return <React.Fragment>
|
|
|
|
|
<ModalHeader>{name}</ModalHeader>
|
|
|
|
|
<ModalContent>
|
|
|
|
|
<StlViewer orbitControls shadows
|
|
|
|
|
url={url}
|
|
|
|
|
style={{height: '80vw', width: '80vw'}}
|
2023-04-05 09:59:29 -06:00
|
|
|
modelProps={{color: '#7353a8'}}
|
2023-04-04 16:17:46 -06:00
|
|
|
/>
|
|
|
|
|
</ModalContent>
|
2023-02-22 11:48:25 -07:00
|
|
|
</React.Fragment>
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
|
2023-05-29 18:04:03 -06:00
|
|
|
function getGenericModal(previewFile) {
|
|
|
|
|
const path = previewFile.primary_path ?? previewFile.path;
|
|
|
|
|
const name = path.replace(/^.*[\\\/]/, '');
|
|
|
|
|
|
|
|
|
|
return <React.Fragment>
|
|
|
|
|
<ModalHeader>{name}</ModalHeader>
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-15 20:58:07 -07:00
|
|
|
export const FilePreviewContext = React.createContext({
|
2023-04-01 11:52:41 -06:00
|
|
|
previewFile: null,
|
|
|
|
|
setPreviewFile: null,
|
|
|
|
|
previewModal: null,
|
|
|
|
|
setPreviewModal: null,
|
|
|
|
|
setCallbacks: null,
|
2023-02-15 20:58:07 -07:00
|
|
|
});
|
|
|
|
|
|
2023-02-15 21:20:23 -07:00
|
|
|
const MAXIMUM_TEXT_SIZE = 5_000_000;
|
|
|
|
|
|
2023-05-03 12:46:57 -06:00
|
|
|
export function FilePreviewProvider({children}) {
|
2023-02-15 21:05:34 -07:00
|
|
|
const [previewFile, setPreviewFile] = React.useState(null);
|
2023-02-15 20:58:07 -07:00
|
|
|
const [previewModal, setPreviewModal] = React.useState(null);
|
2023-04-01 11:52:41 -06:00
|
|
|
const [callbacks, setCallbacks] = React.useState(null);
|
2023-02-15 20:58:07 -07:00
|
|
|
|
2023-04-01 11:52:41 -06:00
|
|
|
const localFetchFile = async () => {
|
|
|
|
|
// Get the file again with its Tags.
|
|
|
|
|
const {path, primary_path} = previewFile;
|
|
|
|
|
try {
|
|
|
|
|
const file = await fetchFile(primary_path ?? path);
|
|
|
|
|
setPreviewFile(file);
|
|
|
|
|
} finally {
|
|
|
|
|
await handleCallbacks();
|
|
|
|
|
}
|
2023-02-15 20:58:07 -07:00
|
|
|
};
|
2023-02-15 17:40:57 -07:00
|
|
|
|
2023-04-10 12:27:55 -06:00
|
|
|
const handleClose = (e) => {
|
|
|
|
|
if (e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
}
|
2023-04-01 11:52:41 -06:00
|
|
|
setPreviewModal(null);
|
|
|
|
|
setPreviewFile(null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleCallbacks = async () => {
|
|
|
|
|
for (const callbacksKey in callbacks) {
|
|
|
|
|
try {
|
|
|
|
|
await callbacks[callbacksKey]();
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-02 16:30:01 -06:00
|
|
|
const localAddTag = async (name) => {
|
2023-05-29 18:04:03 -06:00
|
|
|
await tagFileGroup(previewFile, name);
|
2023-04-02 16:30:01 -06:00
|
|
|
await localFetchFile();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const localRemoveTag = async (name) => {
|
2023-05-29 18:04:03 -06:00
|
|
|
await untagFileGroup(previewFile, name);
|
2023-04-02 16:30:01 -06:00
|
|
|
await localFetchFile();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-29 18:04:03 -06:00
|
|
|
function setModalContent(content, url, downloadURL, path) {
|
|
|
|
|
const openButton = url ? <Button color='blue' as='a' href={url}>Open</Button> : null;
|
2023-04-18 22:03:45 -06:00
|
|
|
const closeButton = <Button onClick={handleClose}>Close</Button>;
|
2023-04-05 14:48:13 -06:00
|
|
|
const tagsDisplay = <TagsSelector selectedTagNames={previewFile['tags']} onAdd={localAddTag}
|
|
|
|
|
onRemove={localRemoveTag}/>;
|
2023-05-29 18:04:03 -06:00
|
|
|
const downloadButton = downloadURL ?
|
|
|
|
|
<Button color='yellow' as='a' href={downloadURL} floated='left'>Download</Button>
|
|
|
|
|
: null;
|
|
|
|
|
const pathContent = path ? <ModalContent>
|
|
|
|
|
<pre>{path}</pre>
|
|
|
|
|
</ModalContent>
|
|
|
|
|
: null;
|
|
|
|
|
console.log('path', path);
|
2023-03-29 13:46:39 -06:00
|
|
|
|
2023-02-15 20:58:07 -07:00
|
|
|
setPreviewModal(<Modal closeIcon
|
|
|
|
|
size='fullscreen'
|
|
|
|
|
open={true}
|
2023-04-10 12:27:55 -06:00
|
|
|
onClose={e => handleClose(e)}
|
2023-02-15 17:40:57 -07:00
|
|
|
>
|
|
|
|
|
{content}
|
2023-05-29 18:04:03 -06:00
|
|
|
{pathContent}
|
2023-02-15 17:40:57 -07:00
|
|
|
<ModalActions>
|
2023-03-29 13:46:39 -06:00
|
|
|
<Media at='mobile'>
|
|
|
|
|
<Grid>
|
|
|
|
|
<Grid.Row>
|
|
|
|
|
<Grid.Column width={6}>{downloadButton}</Grid.Column>
|
|
|
|
|
<Grid.Column width={5}>{openButton}</Grid.Column>
|
|
|
|
|
<Grid.Column width={5}>{closeButton}</Grid.Column>
|
|
|
|
|
</Grid.Row>
|
|
|
|
|
<Grid.Row>
|
|
|
|
|
<Grid.Column width={16}>{tagsDisplay}</Grid.Column>
|
|
|
|
|
</Grid.Row>
|
|
|
|
|
</Grid>
|
|
|
|
|
</Media>
|
|
|
|
|
<Media greaterThanOrEqual='tablet'>
|
|
|
|
|
<Grid>
|
|
|
|
|
<Grid.Row>
|
|
|
|
|
<Grid.Column width={2}>{downloadButton}</Grid.Column>
|
|
|
|
|
<Grid.Column width={10}>{tagsDisplay}</Grid.Column>
|
|
|
|
|
<Grid.Column width={2}>{openButton}</Grid.Column>
|
|
|
|
|
<Grid.Column width={2}>{closeButton}</Grid.Column>
|
|
|
|
|
</Grid.Row>
|
|
|
|
|
</Grid>
|
|
|
|
|
</Media>
|
2023-02-15 17:40:57 -07:00
|
|
|
</ModalActions>
|
|
|
|
|
</Modal>);
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-15 20:58:07 -07:00
|
|
|
React.useEffect(() => {
|
|
|
|
|
setPreviewModal(null);
|
2023-02-15 21:08:47 -07:00
|
|
|
if (previewFile && !_.isObject(previewFile)) {
|
|
|
|
|
console.error(`Unknown previewFile type: ${typeof previewFile}`);
|
2023-05-29 18:04:03 -06:00
|
|
|
toast({
|
|
|
|
|
type: 'error',
|
|
|
|
|
title: 'Cannot preview file',
|
|
|
|
|
description: 'Cannot preview file',
|
|
|
|
|
time: 5000,
|
|
|
|
|
});
|
2023-02-15 21:08:47 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-15 21:05:34 -07:00
|
|
|
if (previewFile && !_.isEmpty(previewFile)) {
|
2023-03-27 11:37:00 -06:00
|
|
|
const {mimetype, size} = previewFile;
|
|
|
|
|
const path = previewFile['primary_path'] || previewFile['path'];
|
2023-05-29 18:04:03 -06:00
|
|
|
console.debug('Previewing file', previewFile);
|
2023-02-22 12:41:41 -07:00
|
|
|
const lowerPath = path.toLowerCase();
|
2023-02-15 21:05:34 -07:00
|
|
|
const url = getMediaPathURL(previewFile);
|
|
|
|
|
const downloadURL = getDownloadPathURL(previewFile);
|
2023-02-15 21:20:23 -07:00
|
|
|
if (mimetype.startsWith('text/') && size > MAXIMUM_TEXT_SIZE) {
|
2023-02-15 17:40:57 -07:00
|
|
|
// Large text files should be downloaded.
|
|
|
|
|
window.open(downloadURL);
|
|
|
|
|
} else if (mimetype.startsWith('text/') || mimetype.startsWith('application/json')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getIframeModal(previewFile), url, downloadURL, path);
|
2023-02-15 17:40:57 -07:00
|
|
|
} else if (mimetype.startsWith('video/')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getVideoModal(previewFile), url, downloadURL, path);
|
2023-02-22 11:48:25 -07:00
|
|
|
} else if (mimetype.startsWith('audio/')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getAudioModal(previewFile), url, downloadURL, path);
|
2023-02-15 17:40:57 -07:00
|
|
|
} else if (mimetype.startsWith('application/epub')) {
|
2023-02-15 21:05:34 -07:00
|
|
|
const viewerURL = getEpubViewerURL(previewFile);
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getEpubModal(previewFile), viewerURL, downloadURL, path);
|
2023-03-29 13:46:39 -06:00
|
|
|
} else if (mimetype.startsWith('application/pdf')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getIframeModal(previewFile), url, downloadURL, path);
|
2023-02-15 17:40:57 -07:00
|
|
|
} else if (mimetype.startsWith('image/')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getImageModal(previewFile), url, null, path);
|
2023-04-04 16:17:46 -06:00
|
|
|
} else if (mimetype.startsWith('model/stl')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getSTLModal(previewFile), null, downloadURL, path);
|
2023-02-22 12:41:41 -07:00
|
|
|
} else if (mimetype.startsWith('application/octet-stream') && lowerPath.endsWith('.mp3')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getAudioModal(previewFile), url, downloadURL, path);
|
2023-04-04 16:17:46 -06:00
|
|
|
} else if (mimetype.startsWith('application/octet-stream') && lowerPath.endsWith('.stl')) {
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getSTLModal(previewFile), null, downloadURL, path);
|
2023-02-15 17:40:57 -07:00
|
|
|
} else {
|
|
|
|
|
// No special handler for this file type, just open it.
|
2023-05-29 18:04:03 -06:00
|
|
|
setModalContent(getGenericModal(previewFile), url, downloadURL, path);
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-02-15 21:05:34 -07:00
|
|
|
}, [previewFile]);
|
2023-02-15 17:40:57 -07:00
|
|
|
|
2023-04-01 11:52:41 -06:00
|
|
|
const value = {previewFile, setPreviewFile, previewModal, setPreviewModal, setCallbacks};
|
|
|
|
|
|
2023-02-15 20:58:07 -07:00
|
|
|
return <FilePreviewContext.Provider value={value}>
|
|
|
|
|
{children}
|
|
|
|
|
{previewModal}
|
|
|
|
|
</FilePreviewContext.Provider>
|
2023-02-15 17:40:57 -07:00
|
|
|
}
|