diff --git a/public/locales/en-US/dashboard.json b/public/locales/en-US/dashboard.json index 6ca3e8c..19ed7a0 100644 --- a/public/locales/en-US/dashboard.json +++ b/public/locales/en-US/dashboard.json @@ -202,6 +202,11 @@ "addViewer": "Add an application", "viewerGroupTitle": "Application group #{{index}}", "viewerType": "Type", + "viewerPlatform": "Platform", + "viewerPlatformDes": "Select the corresponding platform to display the application only on that platform.", + "viewerPlatformPC": "Desktop", + "viewerPlatformMobile": "Mobile", + "viewerPlatformAll": "All", "displayName": "Display name", "displayNameDes": "Display name to users, support i18next key.", "viewerEnabled": "Enabled", diff --git a/public/locales/ja-JP/dashboard.json b/public/locales/ja-JP/dashboard.json index 80b3711..a3f7c92 100644 --- a/public/locales/ja-JP/dashboard.json +++ b/public/locales/ja-JP/dashboard.json @@ -202,6 +202,11 @@ "addViewer": "アプリを追加", "viewerGroupTitle": "アプリグループ #{{index}}", "viewerType": "タイプ", + "viewerPlatform": "プラットフォーム", + "viewerPlatformDes": "対応するプラットフォームを選択し、アプリをそのプラットフォームでのみ表示します。", + "viewerPlatformPC": " パソコン", + "viewerPlatformMobile": "モバイル", + "viewerPlatformAll": "全対応", "displayName": "名称", "displayNameDes": "表示名(i18nextキー対応)", "viewerEnabled": "有効化", diff --git a/public/locales/zh-CN/dashboard.json b/public/locales/zh-CN/dashboard.json index 87bd488..2f6f252 100644 --- a/public/locales/zh-CN/dashboard.json +++ b/public/locales/zh-CN/dashboard.json @@ -202,6 +202,11 @@ "addViewer": "添加应用", "viewerGroupTitle": "应用分组 #{{index}}", "viewerType": "类型", + "viewerPlatform": "平台", + "viewerPlatformDes": "选择对应的平台,使应用仅在对应平台上展示。", + "viewerPlatformPC": "PC端", + "viewerPlatformMobile": "移动端", + "viewerPlatformAll": "全平台", "displayName": "名称", "displayNameDes": "展示名称,支持 i18next 键值。", "viewerEnabled": "启用", diff --git a/public/locales/zh-TW/dashboard.json b/public/locales/zh-TW/dashboard.json index f9e2666..a64228f 100644 --- a/public/locales/zh-TW/dashboard.json +++ b/public/locales/zh-TW/dashboard.json @@ -198,6 +198,11 @@ "addViewer": "新增應用", "viewerGroupTitle": "應用分組 #{{index}}", "viewerType": "型別", + "viewerPlatform": "平台", + "viewerPlatformDes": "選擇對應的平台,讓應用僅在對應平台上展示。", + "viewerPlatformPC": "電腦端", + "viewerPlatformMobile": "行動端", + "viewerPlatformAll": "全平台", "displayName": "名稱", "displayNameDes": "展示名稱,支援 i18next 鍵值。", "viewerEnabled": "啟用", diff --git a/src/api/explorer.ts b/src/api/explorer.ts index 71429d0..0fa09f5 100644 --- a/src/api/explorer.ts +++ b/src/api/explorer.ts @@ -412,6 +412,12 @@ export const ViewerType = { custom: "custom", }; +export enum ViewerPlatform { + pc = "pc", + mobile = "mobile", + all = "all", +} + export interface Viewer { id: string; type: string; @@ -430,6 +436,7 @@ export interface Viewer { }; }; templates?: NewFileTemplate[]; + platform?: ViewerPlatform; } export interface NewFileTemplate { diff --git a/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerEditDialog.tsx b/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerEditDialog.tsx index d61283e..895b20c 100644 --- a/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerEditDialog.tsx +++ b/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerEditDialog.tsx @@ -17,7 +17,7 @@ import Grid from "@mui/material/Grid2"; import { useSnackbar } from "notistack"; import React, { lazy, Suspense, useCallback, useEffect, useMemo, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; -import { Viewer, ViewerType } from "../../../../../api/explorer.ts"; +import { Viewer, ViewerPlatform, ViewerType } from "../../../../../api/explorer.ts"; import { builtInViewers } from "../../../../../redux/thunks/viewer.ts"; import { isTrueVal } from "../../../../../session/utils.ts"; import CircularProgress from "../../../../Common/CircularProgress.tsx"; @@ -110,7 +110,17 @@ interface DraggableTemplateRowProps { template: any; } -function DraggableTemplateRow({ i, moveRow, onExtChange, onNameChange, onDelete, isFirst, isLast, extList, template }: DraggableTemplateRowProps) { +function DraggableTemplateRow({ + i, + moveRow, + onExtChange, + onNameChange, + onDelete, + isFirst, + isLast, + extList, + template, +}: DraggableTemplateRowProps) { const ref = React.useRef(null); const [, drop] = useDrop({ accept: DND_TYPE, @@ -150,11 +160,7 @@ function DraggableTemplateRow({ i, moveRow, onExtChange, onNameChange, onDelete, hover > - + {extList.map((ext) => ( {ext} @@ -163,12 +169,7 @@ function DraggableTemplateRow({ i, moveRow, onExtChange, onNameChange, onDelete, - + @@ -353,6 +354,48 @@ const FileViewerEditDialog = ({ viewer, onChange, open, onClose }: FileViewerEdi {t("settings.maxSizeDes")} + + + + setViewerShadowed((v) => ({ + ...(v as Viewer), + platform: e.target.value as ViewerPlatform, + })) + } + > + + + {t("settings.viewerPlatformPC")} + + + + + {t("settings.viewerPlatformMobile")} + + + + + {t("settings.viewerPlatformAll")} + + + + {t("settings.viewerPlatformDes")} + + {viewer.type == ViewerType.custom && ( { setViewers(group.viewers); }, [group.viewers]); - const moveRow = useCallback((from: number, to: number) => { - if (from === to) return; - const updated = [...viewers]; - const [moved] = updated.splice(from, 1); - updated.splice(to, 0, moved); - setViewers(updated); - onGroupChange({ viewers: updated }); - }, [viewers, onGroupChange]); + const moveRow = useCallback( + (from: number, to: number) => { + if (from === to) return; + const updated = [...viewers]; + const [moved] = updated.splice(from, 1); + updated.splice(to, 0, moved); + setViewers(updated); + onGroupChange({ viewers: updated }); + }, + [viewers, onGroupChange], + ); const handleMoveUp = (idx: number) => { if (idx <= 0) return; moveRow(idx, idx - 1); @@ -178,10 +181,11 @@ const ViewerGroupRow = memo(({ group, index, onDelete, onGroupChange, dndType }: {t("settings.viewerType")} {t("settings.displayName")} {t("settings.exts")} + {t("settings.viewerPlatform")} {t("settings.newFileAction")} {t("settings.viewerEnabled")} - {t("settings.actions")} - + {t("settings.actions")} + diff --git a/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerRow.tsx b/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerRow.tsx index 433c2c8..63a0e07 100644 --- a/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerRow.tsx +++ b/src/component/Admin/Settings/Filesystem/ViewerSetting/FileViewerRow.tsx @@ -1,9 +1,10 @@ import * as React from "react"; import { memo, useCallback, useState } from "react"; -import { Viewer, ViewerType } from "../../../../../api/explorer.ts"; +import { Viewer, ViewerPlatform, ViewerType } from "../../../../../api/explorer.ts"; import { useTranslation } from "react-i18next"; -import { IconButton, TableRow } from "@mui/material"; -import { DenseFilledTextField, NoWrapCell, StyledCheckbox } from "../../../../Common/StyledComponents.tsx"; +import { IconButton, TableRow, ListItemText } from "@mui/material"; +import { DenseFilledTextField, NoWrapCell, StyledCheckbox, DenseSelect } from "../../../../Common/StyledComponents.tsx"; +import { SquareMenuItem } from "../../../../FileManager/ContextMenu/ContextMenu.tsx"; import { ViewerIcon } from "../../../../FileManager/Dialogs/OpenWith.tsx"; import Dismiss from "../../../../Icons/Dismiss.tsx"; import Edit from "../../../../Icons/Edit.tsx"; @@ -23,19 +24,7 @@ export interface FileViewerRowProps { const FileViewerRow = React.memo( React.forwardRef( - ( - { - viewer, - onChange, - onDelete, - onMoveUp, - onMoveDown, - isFirst, - isLast, - style, - }, - ref - ) => { + ({ viewer, onChange, onDelete, onMoveUp, onMoveDown, isFirst, isLast, style }, ref) => { const { t } = useTranslation("dashboard"); const [extCached, setExtCached] = useState(""); const [editOpen, setEditOpen] = useState(false); @@ -43,12 +32,7 @@ const FileViewerRow = React.memo( setEditOpen(false); }, [setEditOpen]); return ( - + @@ -75,6 +59,45 @@ const FileViewerRow = React.memo( onChange={(e) => setExtCached(e.target.value)} /> + + + onChange({ + ...viewer, + platform: e.target.value as ViewerPlatform, + }) + } + > + + + {t("settings.viewerPlatformPC")} + + + + + {t("settings.viewerPlatformMobile")} + + + + + {t("settings.viewerPlatformAll")} + + + + {viewer.templates?.length ? t("settings.nMapping", { num: viewer.templates?.length }) : t("share.none")} @@ -121,8 +144,8 @@ const FileViewerRow = React.memo( ); - } - ) + }, + ), ); export default FileViewerRow; diff --git a/src/component/FileManager/Dialogs/OpenWith.tsx b/src/component/FileManager/Dialogs/OpenWith.tsx index cfc9a43..a8b9fa1 100644 --- a/src/component/FileManager/Dialogs/OpenWith.tsx +++ b/src/component/FileManager/Dialogs/OpenWith.tsx @@ -10,6 +10,8 @@ import { ListItemButton, ListItemText, Stack, + useTheme, + useMediaQuery, } from "@mui/material"; import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -98,6 +100,9 @@ const OpenWith = () => { const [selectedViewer, setSelectedViewer] = React.useState(null); const [expanded, setExpanded] = useState(false); + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down("sm")); + const selectorState = useAppSelector((state) => state.globalState.viewerSelector); useEffect(() => { @@ -174,21 +179,26 @@ const OpenWith = () => { overflow: "auto", }} > - {((expanded ? Object.values(ViewersByID) : selectorState?.viewers) ?? emptyViewer).map((viewer) => ( - openWith(false, viewer)} - onClick={() => onViewerClick(viewer)} - > - - - - - - - - ))} + {((expanded ? Object.values(ViewersByID) : selectorState?.viewers) ?? emptyViewer) + .filter((viewer) => { + const platform = viewer.platform || "all"; + return platform === "all" || platform === (isMobile ? "mobile" : "pc"); + }) + .map((viewer) => ( + openWith(false, viewer)} + onClick={() => onViewerClick(viewer)} + > + + + + + + + + ))} {!expanded && ( setExpanded(true)} disablePadding>