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;