frontend/app/ItemView/ItemView.tsx (162 lines of code) (raw):
import React, {useContext, useEffect, useState} from "react";
import {RouteComponentProps} from "react-router";
import {ArchiveEntry, ObjectGetResponse, StylesMap, UserResponse} from "../types";
import axios from "axios";
import {formatError} from "../common/ErrorViewComponent";
import {CircularProgress, makeStyles, Paper, Snackbar} from "@material-ui/core";
import Helmet from "react-helmet";
import MuiAlert from "@material-ui/lab/Alert";
import clsx from "clsx";
import FlexMetadata from "./FlexMetadata";
import MediaPreview from "../Entry/MediaPreview";
import LightboxInsert from "../Entry/details/LightboxInsert";
import ItemActions from "./ItemActions";
import {extractFileInfo} from "../common/Fileinfo";
import {baseStyles} from "../BaseStyles";
import {UserContext} from "../Context/UserContext";
interface ItemViewParams {
id: string;
}
const useStyles = makeStyles((theme)=>Object.assign({
itemWindow: {
display: "flex",
flexDirection: "column",
height: "95vh"
},
previewArea: {
flexGrow: 1,
flexShrink: 1,
},
infoArea: {
flex: 1,
padding: "1em",
},
inlineThrobber: {
marginRight: "1em"
},
loading: {
verticalAlign: "baseline"
},
title: {
marginLeft: "0.4em"
},
}, baseStyles as StylesMap));
const ItemView:React.FC<RouteComponentProps<ItemViewParams>> = (props) => {
const [entry, setEntry] = useState<ArchiveEntry|undefined>(undefined);
const [loading, setLoading] = useState(true);
const [lastError, setLastError] = useState<string|undefined>(undefined);
const [lastInfo, setLastInfo] = useState<string|undefined>(undefined);
const [showingAlert, setShowingAlert] = useState(false);
const classes = useStyles();
const userContext = useContext(UserContext);
const loadEntry = async (entryId:string) => {
try {
const response = await axios.get<ObjectGetResponse<ArchiveEntry>>(`/api/entry/${entryId}`);
setEntry(response.data.entry);
setLoading(false);
} catch (err) {
console.error("Could not load item ", entryId, ": ", err);
setLoading(false);
setLastError(formatError(err, false));
setShowingAlert(true);
}
}
useEffect(()=>{
if(props.match.params.id && props.match.params.id!="") {
loadEntry(props.match.params.id);
} else {
setLoading(false);
setLastError("Nothing given to load");
setShowingAlert(true);
}
}, []);
const closeAlert = ()=> {
setShowingAlert(false);
}
const proxyGenerationWasTriggered = () => {
setLastInfo("Started proxy generation");
setShowingAlert(true);
}
const subComponentError = (errorDesc:string) => {
setLastError(errorDesc);
setShowingAlert(true);
}
const isInLightbox = () => {
if(!entry || !userContext.profile) return false;
const matchingEntries = entry.lightboxEntries.filter(lbEntry=>lbEntry.owner===userContext.profile?.email);
return matchingEntries.length>0;
}
const itemWasLightboxed = (itemId:string) => {
return new Promise<void>((resolve, reject)=> {
window.setTimeout(() => {
setLoading(true);
loadEntry(itemId)
.then(()=>resolve())
.catch((err)=>{
setLastError(err);
setShowingAlert(true);
reject(err);
})
}, 1000); //if we load immediately the server may not have processed the lightboxing yet
});
}
const fileInfo = entry ? extractFileInfo(entry.path) : undefined;
return <div className={classes.itemWindow}>
<Helmet>
<title>{
fileInfo ? `${fileInfo.filename} - Archive Hunter` : "Archive Hunter"
}</title>
</Helmet>
<Snackbar open={showingAlert} onClose={closeAlert} autoHideDuration={8000}>
<>
{
lastError ? <MuiAlert severity="error" onClose={closeAlert}>{lastError}</MuiAlert> : undefined
}
{
lastInfo ? <MuiAlert severity="info" onClose={closeAlert}>{lastInfo}</MuiAlert> : undefined
}
</>
</Snackbar>
<div className={classes.previewArea}>
{entry ?
<MediaPreview itemId={entry.id}
itemName={entry.path}
mimeType={entry.mimeType}
fileExtension={entry.file_extension ?? ".dat"}
triggeredProxyGeneration={proxyGenerationWasTriggered}
onError={subComponentError}
className={classes.centered}
/> : undefined }
{
loading ?
<span className={clsx(classes.centered, classes.loading)}><CircularProgress className={classes.inlineThrobber}/>Loading...</span> :
undefined
}
{
entry ? <LightboxInsert isInLightbox={isInLightbox()}
entryId={entry.id}
lightboxEntries={entry?.lightboxEntries ?? []}
onError={subComponentError}
lightboxedCb={itemWasLightboxed}
/> : undefined
}
</div>
<div className={classes.infoArea}>
<Paper elevation={3} style={{height: "100%"}}>
{
fileInfo ? <h1 className={classes.title}>{fileInfo.filename}</h1> : undefined
}
{
entry ? <ItemActions storageClass={entry.storageClass}
isInLightbox={isInLightbox()}
itemId={entry.id}
lightboxedCb={itemWasLightboxed}
onError={subComponentError}
/> : undefined
}
{
entry ? <FlexMetadata entry={entry}/> : undefined
}
</Paper>
</div>
</div>
}
export default ItemView;