import React from "react";
import _ from "lodash";
import {Header as SHeader, Icon, Image,} from "semantic-ui-react";
import {TagsSelector} from "../Tags";
import {encodeMediaPath, isSupportedArchive} from "./Common";
import {getFile, tagFileGroup, untagFileGroup} from "../api";
import {ArchivePreviewContent} from "./ArchivePreview";
import {CbzViewer} from "./CbzViewer";
import {StlViewer} from "react-stl-viewer";
import {Button, Modal} from "./Theme";
import {toast} from "react-semantic-toasts-2";
import {useOneQuery} from "../hooks/customHooks";
import {ShareButton} from "./Share";
import {pathDirectory} from "./FileBrowser";
import {InlineErrorBoundary} from "./ErrorBoundary";
import {useLocation} from "react-router";
// Routes where file views should not be tracked
const EXCLUDED_TRACKING_ROUTES = [
/^\/videos\/video\//,
/^\/videos\/channel\/[^/]+\/video\//,
/^\/archive\/\d+/
];
function shouldSkipTracking(pathname) {
return EXCLUDED_TRACKING_ROUTES.some(pattern => pattern.test(pathname));
}
function getMediaPathURL(previewFile) {
if (previewFile['primary_path']) {
return `/media/${encodeMediaPath(previewFile['primary_path'])}`;
} else {
return `/media/${encodeMediaPath(previewFile['path'])}`;
}
}
function getDownloadPathURL(previewFile) {
if (previewFile['primary_path']) {
return `/download/${encodeMediaPath(previewFile['primary_path'])}`;
} else {
return `/download/${encodeMediaPath(previewFile['path'])}`;
}
}
function getEpubViewerURL(previewFile) {
let url = previewFile['primary_path'] ?? previewFile['path'];
return `/epub/epub.html?url=/media/${encodeMediaPath(url)}`
}
function getIframePreviewModal(previewFile, url) {
url = url || getMediaPathURL(previewFile);
return
}
function getImagePreviewModal(previewFile) {
const path = previewFile.primary_path ?? previewFile.path;
const url = getMediaPathURL(previewFile);
// Get the file name from the absolute path.
const name = path.replace(/^.*[\\\/]/, '');
return
{name}
}
function getGenericPreviewModal(previewFile) {
// No special handler for this file type, just open it.
const path = previewFile.primary_path ?? previewFile.path;
const name = path.replace(/^.*[\\\/]/, '');
return {name}
}
export const FilePreviewContext = React.createContext({
previewFile: null,
setPreviewFile: null,
previewModal: null,
setPreviewModal: null,
setCallbacks: null,
});
const MAXIMUM_TEXT_SIZE = 5_000_000;
export function FilePreviewProvider({children}) {
// Data about the file that is being previewed.
const [previewFile, setPreviewFile] = React.useState(null);
// A modal is only displayed once we have data about the file.
const [previewModal, setPreviewModal] = React.useState(null);
const [callbacks, setCallbacks] = React.useState(null);
// The file path from the `preview` URL query.
const [previewQuery, setPreviewQuery] = useOneQuery('preview');
const [errorModalOpen, setErrorModalOpen] = React.useState(false);
const location = useLocation();
// Determine if tracking should be skipped based on current route
const skipTracking = shouldSkipTracking(location.pathname);
const handleErrorModalClose = () => {
setPreviewQuery(null);
setErrorModalOpen(false);
}
const errorModal = Unknown FileCannot display preview, no such file exists: {previewQuery}
React.useEffect(() => {
if (!_.isEmpty(previewFile)) {
// Change URL to match the file that is being previewed.
setPreviewQuery(previewFile['primary_path'] || previewFile['path']);
}
}, [previewFile]);
const initPreviewFile = async () => {
// Get simple information about the file, preview the file.
try {
const file = await getFile(previewQuery, skipTracking);
setPreviewFile(file);
} catch (e) {
console.error(e);
setPreviewModal(errorModal);
setErrorModalOpen(true);
}
}
React.useEffect(() => {
// Navigated to a page which has a file preview active, fetch the information about the file, then display it.
if (previewQuery && !previewFile) {
initPreviewFile();
} else if (!previewQuery && (previewFile || previewModal)) {
// User navigated back/forward and preview query is gone, close the preview.
// Only close if there's actually a preview open (previewFile or previewModal exists).
setPreviewFile(null);
setPreviewModal(null);
}
}, [previewQuery]);
const localFetchFile = async () => {
// Get the file again with its Tags.
const {path, primary_path} = previewFile;
try {
const file = await getFile(primary_path ?? path, skipTracking);
setPreviewFile(file);
} finally {
await handleCallbacks();
}
};
const handleClose = (e) => {
if (e) {
e.preventDefault();
}
setPreviewModal(null);
setPreviewFile(null);
setPreviewQuery(null);
}
const handleCallbacks = async () => {
for (const callbacksKey in callbacks) {
try {
await callbacks[callbacksKey]();
} catch (e) {
console.error(e);
}
}
}
const localAddTag = async (name) => {
await tagFileGroup(previewFile, name);
await localFetchFile();
}
const localRemoveTag = async (name) => {
await untagFileGroup(previewFile, name);
await localFetchFile();
}
function setModalContent(content, url, downloadURL, path, taggable = true) {
// All action buttons share `size='small'` and `icon` for consistent height across viewports.
// Close is handled by the Modal's `closeIcon` (X in the top-right corner) — no dedicated button.
const openButton = url
?
: null;
const tagsDisplay = taggable ?
: null;
const downloadButton = downloadURL
?
: null;
const directoryURL = path ? `/files?folders=${encodeURIComponent(pathDirectory(path))}` : null;
const directoryButton = path ? : null;
const pathContent = path ?