fix(file apps): "open in new tab" option now applies to all app types (cloudreve/cloudreve#2647)

This commit is contained in:
Aaron Liu 2025-07-15 11:23:19 +08:00
parent aa80ca5bb2
commit 3a6a22bb45
4 changed files with 84 additions and 29 deletions

View File

@ -17,6 +17,8 @@ import FormControl from "@mui/material/FormControl";
import Grid from "@mui/material/Grid2";
import { useSnackbar } from "notistack";
import React, { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { Trans, useTranslation } from "react-i18next";
import { Viewer, ViewerPlatform, ViewerType } from "../../../../api/explorer.ts";
import { builtInViewers } from "../../../../redux/thunks/viewer.ts";
@ -35,13 +37,11 @@ import DraggableDialog from "../../../Dialogs/DraggableDialog.tsx";
import { SquareMenuItem } from "../../../FileManager/ContextMenu/ContextMenu.tsx";
import { ViewerIDWithDefaultIcons } from "../../../FileManager/Dialogs/OpenWith.tsx";
import Add from "../../../Icons/Add.tsx";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import Dismiss from "../../../Icons/Dismiss.tsx";
import SettingForm from "../../../Pages/Setting/SettingForm.tsx";
import MagicVarDialog, { MagicVar } from "../../Common/MagicVarDialog.tsx";
import { NoMarginHelperText } from "../../Settings/Settings.tsx";
import ArrowDown from "../../../Icons/ArrowDown.tsx";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
const MonacoEditor = lazy(() => import("../../../Viewers/CodeViewer/MonacoEditor.tsx"));
@ -396,28 +396,26 @@ const FileViewerEditDialog = ({ viewer, onChange, open, onClose }: FileViewerEdi
<NoMarginHelperText>{t("settings.viewerPlatformDes")}</NoMarginHelperText>
</FormControl>
</SettingForm>
{viewer.type == ViewerType.custom && (
<SettingForm noContainer lgWidth={6}>
<FormControlLabel
control={
<Switch
checked={isTrueVal(viewerShadowed.props?.openInNew ?? "")}
onChange={(e) =>
setViewerShadowed((v) => ({
...(v as Viewer),
props: {
...(v?.props ?? {}),
openInNew: e.target.checked.toString(),
},
}))
}
/>
}
label={t("settings.openInNew")}
/>
<NoMarginHelperText>{t("settings.openInNewDes")}</NoMarginHelperText>
</SettingForm>
)}
<SettingForm noContainer lgWidth={6}>
<FormControlLabel
control={
<Switch
checked={isTrueVal(viewerShadowed.props?.openInNew ?? "")}
onChange={(e) =>
setViewerShadowed((v) => ({
...(v as Viewer),
props: {
...(v?.props ?? {}),
openInNew: e.target.checked.toString(),
},
}))
}
/>
}
label={t("settings.openInNew")}
/>
<NoMarginHelperText>{t("settings.openInNewDes")}</NoMarginHelperText>
</SettingForm>
{viewer.id == builtInViewers.drawio && (
<SettingForm noContainer title={t("settings.drawioHost")} lgWidth={6}>
<DenseFilledTextField

View File

@ -1,7 +1,13 @@
import { useEffect } from "react";
import { FileManagerIndex } from "../component/FileManager/FileManager.tsx";
import { useAppDispatch, useAppSelector } from "../redux/hooks.ts";
import { beforePathChange, checkReadMeEnabled, navigateReconcile, setTargetPath } from "../redux/thunks/filemanager.ts";
import {
beforePathChange,
checkOpenViewerQuery,
checkReadMeEnabled,
navigateReconcile,
setTargetPath,
} from "../redux/thunks/filemanager.ts";
import { useQuery } from "../util";
import { Filesystem } from "../util/uri.ts";
@ -32,6 +38,7 @@ const useNavigation = (index: number, initialPath?: string) => {
if (path) {
dispatch(navigateReconcile(index)).then(() => {
dispatch(checkReadMeEnabled(index));
dispatch(checkOpenViewerQuery(index));
});
dispatch(beforePathChange(index));
}

View File

@ -49,10 +49,11 @@ import {
setShareReadmeDetect,
setUploadFromClipboardDialog,
} from "../globalStateSlice.ts";
import { Viewers } from "../siteConfigSlice.ts";
import { Viewers, ViewersByID } from "../siteConfigSlice.ts";
import { AppThunk } from "../store.ts";
import { deleteFile, openFileContextMenu } from "./file.ts";
import { queueLoadShareInfo } from "./share.ts";
import { openViewer } from "./viewer.ts";
export function setTargetPath(index: number, path: string): AppThunk {
return async (dispatch, _getState) => {
@ -128,6 +129,39 @@ export function checkReadMeEnabled(index: number): AppThunk {
};
}
export function checkOpenViewerQuery(index: number): AppThunk {
return async (dispatch, getState) => {
const currentUrl = new URL(window.location.href);
const viewer = currentUrl.searchParams.get("viewer");
const fileId = currentUrl.searchParams.get("open");
const version = currentUrl.searchParams.get("version");
const size = currentUrl.searchParams.get("size");
// Clear viewer-related query parameters
currentUrl.searchParams.delete("viewer");
currentUrl.searchParams.delete("open");
currentUrl.searchParams.delete("version");
currentUrl.searchParams.delete("size");
window.history.replaceState({}, "", currentUrl.toString());
if (!fileId || !viewer || !ViewersByID[viewer]) {
return;
}
const { files: list, pagination } = getState().fileManager[index]?.list ?? {};
if (list) {
// Find readme file from highest to lowest priority
const found = list.find((file) => file.id === fileId);
if (found) {
dispatch(openViewer(found, ViewersByID[viewer], parseInt(size ?? "0"), version ?? undefined, true));
return;
}
}
alert("openViewer");
};
}
export function navigateReconcile(index: number, opt?: NavigateReconcileOptions): AppThunk<Promise<void>> {
return async (dispatch, getState) => {
const timeNow = dayjs().valueOf();

View File

@ -9,6 +9,7 @@ import { FileManagerIndex } from "../../component/FileManager/FileManager.tsx";
import SessionManager, { UserSettings } from "../../session";
import { isTrueVal } from "../../session/utils.ts";
import { dataUrlToBytes, fileExtension, fileNameNoExt, getFileLinkedUri, sizeToString } from "../../util";
import { base64Encode } from "../../util/base64.ts";
import CrUri from "../../util/uri.ts";
import { closeContextMenu, ContextMenuTypes, fileUpdated } from "../fileManagerSlice.ts";
import {
@ -34,7 +35,6 @@ import { Viewers, ViewersByID } from "../siteConfigSlice.ts";
import { AppThunk } from "../store.ts";
import { askSaveAs, askStaleVersionAction } from "./dialog.ts";
import { longRunningTaskWithSnackbar, refreshSingleFileSymbolicLinks } from "./file.ts";
import { base64Encode } from "../../util/base64.ts";
export interface ExpandedViewerSetting {
[key: string]: Viewer[];
@ -98,8 +98,24 @@ export function openViewers(
};
}
export function openViewer(file: FileResponse, viewer: Viewer, size: number, preferredVersion?: string): AppThunk {
export function openViewer(
file: FileResponse,
viewer: Viewer,
size: number,
preferredVersion?: string,
forceNotOpenInNew?: boolean,
): AppThunk {
return async (dispatch, getState) => {
if (!forceNotOpenInNew && viewer.type != ViewerType.custom && isTrueVal(viewer.props?.openInNew ?? "")) {
const currentUrl = new URL(window.location.href);
currentUrl.searchParams.set("viewer", viewer.id ?? "");
currentUrl.searchParams.set("version", preferredVersion ?? "");
currentUrl.searchParams.set("open", file.id ?? "");
currentUrl.searchParams.set("size", size.toString());
window.open(currentUrl.toString(), "_blank");
return;
}
// Warning for large file
if (viewer.max_size && size > viewer.max_size) {
enqueueSnackbar({