diff --git a/public/locales/de-DE/application.json b/public/locales/de-DE/application.json index 0822837..e9b8bb5 100644 --- a/public/locales/de-DE/application.json +++ b/public/locales/de-DE/application.json @@ -171,6 +171,8 @@ "googledocs": "Google Docs Online-Reader", "m365viewer": "Microsoft Office Online-Reader", "pdfViewer": "PDF-Reader", + "archivePreview": "Archiv-Vorschau", + "extractSelected": "Ausgewählte Dateien extrahieren", "viewerFileSizeWarning": "Die geöffnete Dateigröße ({{file_size}}) überschreitet das Limit von {{app}} ({{max}}) und funktioniert möglicherweise nicht ordnungsgemäß.", "testSubtitleStyle": "Untertitel-Stil testen AaBbCc", "color": "Farbe", diff --git a/public/locales/en-US/application.json b/public/locales/en-US/application.json index b425af7..9742574 100644 --- a/public/locales/en-US/application.json +++ b/public/locales/en-US/application.json @@ -171,6 +171,8 @@ "googledocs": "Google Docs Viewer", "m365viewer": "Microsoft Office Online Viewer", "pdfViewer": "PDF Viewer", + "archivePreview": "Archive Preview", + "extractSelected": "Extract Selected Files", "viewerFileSizeWarning": "Size of opened file ({{file_size}}) exceed limit ({{max}}) of {{app}}, it might not work properly.", "testSubtitleStyle": "Test subtitle style AaBbCc", "color": "Color", diff --git a/public/locales/es-ES/application.json b/public/locales/es-ES/application.json index e62fa4f..858fda1 100644 --- a/public/locales/es-ES/application.json +++ b/public/locales/es-ES/application.json @@ -171,6 +171,8 @@ "googledocs": "Visor de Google Docs", "m365viewer": "Visor en línea de Microsoft Office", "pdfViewer": "Visor PDF", + "archivePreview": "Vista previa del archivo", + "extractSelected": "Extraer archivos seleccionados", "viewerFileSizeWarning": "El tamaño del archivo abierto ({{file_size}}) excede el límite ({{max}}) de {{app}}, podría no funcionar correctamente.", "testSubtitleStyle": "Probar estilo de subtítulos AaBbCc", "color": "Color", diff --git a/public/locales/fr-FR/application.json b/public/locales/fr-FR/application.json index f51f7b6..540fb47 100644 --- a/public/locales/fr-FR/application.json +++ b/public/locales/fr-FR/application.json @@ -171,6 +171,8 @@ "googledocs": "Visionneuse Google Docs", "m365viewer": "Visionneuse Microsoft Office Online", "pdfViewer": "Visionneuse PDF", + "archivePreview": "Aperçu de l'archive", + "extractSelected": "Extraire les fichiers sélectionnés", "viewerFileSizeWarning": "La taille du fichier ouvert ({{file_size}}) dépasse la limite ({{max}}) de {{app}}, il pourrait ne pas fonctionner correctement.", "testSubtitleStyle": "Tester le style de sous-titres AaBbCc", "color": "Couleur", diff --git a/public/locales/it-IT/application.json b/public/locales/it-IT/application.json index 2e18a47..63a29c0 100644 --- a/public/locales/it-IT/application.json +++ b/public/locales/it-IT/application.json @@ -171,6 +171,8 @@ "googledocs": "Visualizzatore Google Docs", "m365viewer": "Visualizzatore Microsoft Office Online", "pdfViewer": "Visualizzatore PDF", + "archivePreview": "Anteprima archivio", + "extractSelected": "Estrai file selezionati", "viewerFileSizeWarning": "La dimensione del file aperto ({{file_size}}) supera il limite ({{max}}) di {{app}}, potrebbe non funzionare correttamente.", "testSubtitleStyle": "Testa stile sottotitoli AaBbCc", "color": "Colore", diff --git a/public/locales/ja-JP/application.json b/public/locales/ja-JP/application.json index 272cd44..8bc58a7 100644 --- a/public/locales/ja-JP/application.json +++ b/public/locales/ja-JP/application.json @@ -171,6 +171,8 @@ "googledocs": "Googleドキュメント オンラインリーダー", "m365viewer": "Microsoft Office オンラインリーダー", "pdfViewer": "PDFリーダー", + "archivePreview": "アーカイブプレビュー", + "extractSelected": "選択したファイルを展開", "viewerFileSizeWarning": "ファイルサイズ({{file_size}})が{{app}}の制限({{max}})を超えているため、正常に動作しない可能性があります。", "testSubtitleStyle": "字幕スタイルのテスト AaBbCc", "color": "色", diff --git a/public/locales/ko-KR/application.json b/public/locales/ko-KR/application.json index d9e2678..8ab5dfd 100644 --- a/public/locales/ko-KR/application.json +++ b/public/locales/ko-KR/application.json @@ -171,6 +171,8 @@ "googledocs": "Google Docs 온라인 뷰어", "m365viewer": "Microsoft Office 온라인 뷰어", "pdfViewer": "PDF 뷰어", + "archivePreview": "아카이브 미리보기", + "extractSelected": "선택한 파일 압축 해제", "viewerFileSizeWarning": "열린 파일 크기({{file_size}})가 {{app}}의 제한({{max}})을 초과하여 정상적으로 작동하지 않을 수 있습니다.", "testSubtitleStyle": "자막 스타일 테스트 AaBbCc", "color": "색상", diff --git a/public/locales/pt-BR/application.json b/public/locales/pt-BR/application.json index 767b0b0..c251f19 100644 --- a/public/locales/pt-BR/application.json +++ b/public/locales/pt-BR/application.json @@ -171,6 +171,8 @@ "googledocs": "Visualizador Google Docs", "m365viewer": "Visualizador Microsoft Office Online", "pdfViewer": "Visualizador PDF", + "archivePreview": "Visualização de arquivo", + "extractSelected": "Extrair arquivos selecionados", "viewerFileSizeWarning": "Tamanho do arquivo aberto ({{file_size}}) excede o limite ({{max}}) do {{app}}, pode não funcionar corretamente.", "testSubtitleStyle": "Testar estilo de legenda AaBbCc", "color": "Cor", diff --git a/public/locales/ru-RU/application.json b/public/locales/ru-RU/application.json index 04355c2..ee40f56 100644 --- a/public/locales/ru-RU/application.json +++ b/public/locales/ru-RU/application.json @@ -171,6 +171,8 @@ "googledocs": "Онлайн-просмотрщик Google Docs", "m365viewer": "Онлайн-просмотрщик Microsoft Office", "pdfViewer": "Просмотрщик PDF", + "archivePreview": "Предварительный просмотр архива", + "extractSelected": "Извлечь выбранные файлы", "viewerFileSizeWarning": "Размер открываемого файла ({{file_size}}) превышает лимит {{app}} ({{max}}), возможно, он не будет работать корректно.", "testSubtitleStyle": "Тест стиля субтитров AaBbCc", "color": "Цвет", diff --git a/public/locales/zh-CN/application.json b/public/locales/zh-CN/application.json index 709b7b0..c9ab89a 100644 --- a/public/locales/zh-CN/application.json +++ b/public/locales/zh-CN/application.json @@ -171,6 +171,8 @@ "googledocs": "Google Docs 在线阅读器", "m365viewer": "Microsoft Office 在线阅读器", "pdfViewer": "PDF 阅读器", + "archivePreview": "压缩包预览", + "extractSelected": "解压缩选中的文件", "viewerFileSizeWarning": "打开的文件大小 ({{file_size}}) 超过了 {{app}} 的限制 ({{max}}),可能无法正常工作。", "testSubtitleStyle": "测试字幕样式 AaBbCc", "color": "颜色", diff --git a/public/locales/zh-CN/dashboard.json b/public/locales/zh-CN/dashboard.json index 52c946b..ac10608 100644 --- a/public/locales/zh-CN/dashboard.json +++ b/public/locales/zh-CN/dashboard.json @@ -1116,7 +1116,7 @@ "allowWabDAVDes": "关闭后,用户无法通过 WebDAV 协议连接至网盘。", "allowWabDAVProxy": "WebDAV 代理", "allowWabDAVProxyDes": "启用后,用户可以配置 WebDAV 下载经由 Cloudreve 中转。", - "allowCompressTask": "压缩/解压缩任务", + "compressTask": "压缩/解压缩任务", "compressTaskDes": "开启后,用户可以在线压缩/解压缩文件。", "compressSize": "待压缩文件最大大小", "compressSizeDes": "用户可创建的压缩任务的文件最大总大小,填写为 0 表示不限制。这一限制在创建压缩任务时不会检查,当执行时已处理原始文件总大小超过此限制时,任务会失败。", diff --git a/public/locales/zh-TW/application.json b/public/locales/zh-TW/application.json index e43d811..8312762 100644 --- a/public/locales/zh-TW/application.json +++ b/public/locales/zh-TW/application.json @@ -171,6 +171,8 @@ "googledocs": "Google Docs 線上閱讀器", "m365viewer": "Microsoft Office 線上閱讀器", "pdfViewer": "PDF 閱讀器", + "archivePreview": "壓縮包預覽", + "extractSelected": "解壓縮選中的檔案", "viewerFileSizeWarning": "開啟的檔案大小 ({{file_size}}) 超過了 {{app}} 的限制 ({{max}}),可能無法正常工作。", "testSubtitleStyle": "測試字幕樣式 AaBbCc", "color": "顏色", diff --git a/public/locales/zh-TW/dashboard.json b/public/locales/zh-TW/dashboard.json index ad13ef7..ce421c8 100644 --- a/public/locales/zh-TW/dashboard.json +++ b/public/locales/zh-TW/dashboard.json @@ -1116,7 +1116,7 @@ "allowWabDAVDes": "關閉後,使用者無法通過 WebDAV 協議連線至網盤。", "allowWabDAVProxy": "WebDAV 代理", "allowWabDAVProxyDes": "啟用後, 使用者可以配置 WebDAV 下載經由 Cloudreve 中轉。", - "allowCompressTask": "壓縮/解壓縮任務", + "compressTask": "壓縮/解壓縮任務", "compressTaskDes": "開啟後,使用者可以線上壓縮/解壓縮檔案。", "compressSize": "待壓縮檔案最大大小", "compressSizeDes": "使用者可建立的壓縮任務的檔案最大總大小,填寫為 0 表示不限制。這一限制在建立壓縮任務時不會檢查,當執行時已處理原始檔案總大小超過此限制時,任務會失敗。", diff --git a/src/api/api.ts b/src/api/api.ts index cd4bbc4..efaff02 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -40,6 +40,8 @@ import { User as UserEnt, } from "./dashboard.ts"; import { + ArchiveListFilesResponse, + ArchiveListFilesService, CreateFileService, CreateViewerSessionService, DeleteFileService, @@ -2007,3 +2009,17 @@ export function sendCleanupTask(args: CleanupTaskService): ThunkResponse { ); }; } + +export function getArchiveListFiles(args: ArchiveListFilesService): ThunkResponse { + return async (dispatch, _getState) => { + return await dispatch( + send( + `/file/archive`, + { method: "GET", params: args }, + { + ...defaultOpts, + }, + ), + ); + }; +} diff --git a/src/api/explorer.ts b/src/api/explorer.ts index 660d4bd..4a471ad 100644 --- a/src/api/explorer.ts +++ b/src/api/explorer.ts @@ -447,6 +447,7 @@ export interface Viewer { }; templates?: NewFileTemplate[]; platform?: ViewerPlatform; + required_group_permission?: number[]; } export interface NewFileTemplate { @@ -548,3 +549,19 @@ export enum CustomPropsType { link = "link", rating = "rating", } + +export interface ArchivedFile { + name: string; + size: number; + updated_at?: string; + is_directory: boolean; +} + +export interface ArchiveListFilesResponse { + files: ArchivedFile[]; +} + +export interface ArchiveListFilesService { + uri: string; + entity?: string; +} diff --git a/src/api/workflow.ts b/src/api/workflow.ts index f6c0852..94c7536 100644 --- a/src/api/workflow.ts +++ b/src/api/workflow.ts @@ -5,6 +5,7 @@ export interface ArchiveWorkflowService { dst: string; encoding?: string; password?: string; + file_mask?: string[]; } export interface TaskListResponse { diff --git a/src/component/FileManager/Dialogs/Dialogs.tsx b/src/component/FileManager/Dialogs/Dialogs.tsx index d7e42c0..a836765 100644 --- a/src/component/FileManager/Dialogs/Dialogs.tsx +++ b/src/component/FileManager/Dialogs/Dialogs.tsx @@ -13,6 +13,7 @@ import SaveAs from "./SaveAs.tsx"; import Photopea from "../../Viewers/Photopea/Photopea.tsx"; import OpenWith from "./OpenWith.tsx"; import Wopi from "../../Viewers/Wopi.tsx"; +import ArchivePreview from "../../Viewers/ArchivePreview/ArchivePreview.tsx"; import CodeViewer from "../../Viewers/CodeViewer/CodeViewer.tsx"; import DrawIOViewer from "../../Viewers/DrawIO/DrawIOViewer.tsx"; import MarkdownViewer from "../../Viewers/MarkdownEditor/MarkdownViewer.tsx"; @@ -41,6 +42,7 @@ const Dialogs = () => { const directLink = useAppSelector((state) => state.globalState.directLinkDialogOpen); const excalidrawViewer = useAppSelector((state) => state.globalState.excalidrawViewer); const directLinkManagement = useAppSelector((state) => state.globalState.directLinkManagementDialogOpen); + const archivePreview = useAppSelector((state) => state.globalState.archiveViewer); return ( <> @@ -75,6 +77,7 @@ const Dialogs = () => { {directLink != undefined && } {excalidrawViewer != undefined && } {directLinkManagement != undefined && } + {archivePreview != undefined && } ); }; diff --git a/src/component/FileManager/Dialogs/ExtractArchive.tsx b/src/component/FileManager/Dialogs/ExtractArchive.tsx index 772a8ac..d1d6d58 100644 --- a/src/component/FileManager/Dialogs/ExtractArchive.tsx +++ b/src/component/FileManager/Dialogs/ExtractArchive.tsx @@ -83,6 +83,7 @@ const ExtractArchive = () => { const open = useAppSelector((state) => state.globalState.extractArchiveDialogOpen); const target = useAppSelector((state) => state.globalState.extractArchiveDialogFile); const current = useAppSelector((state) => state.fileManager[FileManagerIndex.main].pure_path); + const mask = useAppSelector((state) => state.globalState.extractArchiveDialogMask); const showEncodingOption = useMemo(() => { const ext = fileExtension(target?.name ?? ""); @@ -116,6 +117,7 @@ const ExtractArchive = () => { dst: path, encoding: showEncodingOption && encoding != defaultEncodingValue ? encoding : undefined, password: showPasswordOption && password ? password : undefined, + file_mask: mask ?? undefined, }), ) .then(() => { @@ -129,7 +131,7 @@ const ExtractArchive = () => { .finally(() => { setLoading(false); }); - }, [target, encoding, path, showPasswordOption, showEncodingOption, password]); + }, [target, encoding, path, showPasswordOption, showEncodingOption, password, mask]); return ( { @@ -62,6 +64,8 @@ export const ViewerIcon = ({ viewer, size = 32, py = 0.5 }: ViewerIconProps) => return ; case builtInViewers.music: return ; + case builtInViewers.archive: + return ; case builtInViewers.markdown: return ( { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const theme = useTheme(); + const { enqueueSnackbar } = useSnackbar(); + const viewerState = useAppSelector((state) => state.globalState.archiveViewer); + + const [loading, setLoading] = useState(false); + const [files, setFiles] = useState([]); + const [currentPath, setCurrentPath] = useState(""); + const [selectedFiles, setSelectedFiles] = useState([]); + const [filterText, setFilterText] = useState(""); + const [height, setHeight] = useState(33); + + const currentFiles = useMemo(() => { + if (!files) return []; + + if (!currentPath) { + return files.filter((file) => !file.name.includes("/")); + } + + // 如果在子目录,显示该目录下的文件和文件夹 + const pathPrefix = currentPath.endsWith("/") ? currentPath : currentPath + "/"; + const pathFiles = files.filter((file) => file.name.startsWith(pathPrefix) && file.name !== currentPath); + + // 去重并转换为相对路径 + const relativePaths = new Set(); + const result: ArchivedFile[] = []; + + pathFiles.forEach((file) => { + const relativePath = file.name.substring(pathPrefix.length); + const firstSlash = relativePath.indexOf("/"); + + if (firstSlash === -1) { + if (!relativePaths.has(relativePath)) { + relativePaths.add(relativePath); + result.push({ + ...file, + name: relativePath, + }); + } + } else { + const dirName = relativePath.substring(0, firstSlash); + if (!relativePaths.has(dirName)) { + relativePaths.add(dirName); + result.push({ + name: dirName, + size: 0, + updated_at: file.updated_at, + is_directory: true, + }); + } + } + }); + + return result; + }, [files, currentPath]); + + // 过滤文件 + const filteredFiles = useMemo(() => { + if (!filterText) return currentFiles; + return currentFiles.filter((file) => file.name.toLowerCase().includes(filterText.toLowerCase())); + }, [currentFiles, filterText]); + + // 面包屑路径 + const breadcrumbPaths = useMemo(() => { + if (!currentPath) return []; + return currentPath.split("/").filter(Boolean); + }, [currentPath]); + + useEffect(() => { + if (!viewerState || !viewerState.open) { + return; + } + + setLoading(true); + setFiles([]); + setCurrentPath(""); + setSelectedFiles([]); + setFilterText(""); + + dispatch( + getArchiveListFiles({ + uri: getFileLinkedUri(viewerState.file), + entity: viewerState.version, + }), + ) + .then((res) => { + if (res.files) { + setFiles(res.files); + } + }) + .catch(() => { + onClose(); + }) + .finally(() => { + setLoading(false); + }); + }, [viewerState]); + + const onClose = useCallback(() => { + dispatch(closeArchiveViewer()); + }, [dispatch]); + + const navigateToDirectory = useCallback( + (dirName: string) => { + if (!currentPath) { + setCurrentPath(dirName); + } else { + setCurrentPath(currentPath + "/" + dirName); + } + setSelectedFiles([]); + }, + [currentPath], + ); + + const navigateToBreadcrumb = useCallback( + (index: number) => { + if (index === -1) { + setCurrentPath(""); + } else { + const newPath = breadcrumbPaths.slice(0, index + 1).join("/"); + setCurrentPath(newPath); + } + setSelectedFiles([]); + }, + [breadcrumbPaths], + ); + + const toggleFileSelection = useCallback( + (fileName: string) => { + const fullPath = currentPath ? currentPath + "/" + fileName : fileName; + setSelectedFiles((prev) => { + if (prev.includes(fullPath)) { + return prev.filter((f) => f !== fullPath); + } else { + return [...prev, fullPath]; + } + }); + }, + [currentPath], + ); + + const toggleSelectAll = useCallback(() => { + const allFiles = filteredFiles.map((file) => (currentPath ? currentPath + "/" + file.name : file.name)); + + const allSelected = allFiles.every((file) => selectedFiles.includes(file)); + + if (allSelected) { + setSelectedFiles((prev) => prev.filter((file) => !allFiles.includes(file))); + } else { + setSelectedFiles((prev) => [...new Set([...prev, ...allFiles])]); + } + }, [filteredFiles, selectedFiles, currentPath]); + + // 解压选中的文件 + const extractSelectedFiles = useCallback(() => { + if (selectedFiles.length === 0) { + return; + } + + dispatch(setExtractArchiveDialog({ open: true, file: viewerState?.file, mask: selectedFiles })); + }, [selectedFiles, t, enqueueSnackbar]); + + const extractArchive = useCallback(() => { + if (!viewerState?.file) { + return; + } + dispatch(setExtractArchiveDialog({ open: true, file: viewerState?.file })); + }, [viewerState?.file]); + + return ( + <> + + {loading && } + {!loading && ( + + + }> + navigateToBreadcrumb(-1)} + sx={{ + display: "flex", + alignItems: "center", + textDecoration: "none", + "&:hover": { textDecoration: "underline" }, + }} + > + + {t("fileManager.rootFolder")} + + {breadcrumbPaths.map((path, index) => { + const isLast = index === breadcrumbPaths.length - 1; + return isLast ? ( + + + {path} + + ) : ( + navigateToBreadcrumb(index)} + sx={{ + display: "flex", + alignItems: "center", + textDecoration: "none", + "&:hover": { textDecoration: "underline" }, + }} + > + + {path} + + ); + })} + + + + {filteredFiles.length > 0 ? ( + + { + setHeight(h + 0.5); + }} + components={{ + // eslint-disable-next-line react/display-name + Table: (props) => , + }} + data={filteredFiles} + itemContent={(_index, file) => { + const fullPath = currentPath ? currentPath + "/" + file.name : file.name; + const isSelected = selectedFiles.includes(fullPath); + + return ( + <> + + toggleFileSelection(file.name)} + size="small" + /> + + + + + {file.is_directory ? ( + navigateToDirectory(file.name)} + sx={{ + color: "primary.main", + fontWeight: 500, + textDecoration: "none", + background: "none", + border: "none", + cursor: "pointer", + padding: 0, + "&:hover": { textDecoration: "underline" }, + }} + > + {fileBase(file.name)} + + ) : ( + + {fileBase(file.name)} + + )} + + + + + {file.is_directory ? "-" : sizeToString(file.size)} + + + + + {file.updated_at ? : "-"} + + + + ); + }} + /> + + ) : ( + + {t("fileManager.nothingFound")} + + )} + + {!viewerState?.version && ( + + + {selectedFiles.length > 0 && ( + + {t("fileManager.extractSelected")} + + )} + + )} + + )} + + + ); +}; + +export default ArchivePreview; diff --git a/src/redux/globalStateSlice.ts b/src/redux/globalStateSlice.ts index 1e46f1a..b0939d4 100644 --- a/src/redux/globalStateSlice.ts +++ b/src/redux/globalStateSlice.ts @@ -178,6 +178,7 @@ export interface GlobalStateSlice { // Extract archive dialog extractArchiveDialogOpen?: boolean; extractArchiveDialogFile?: FileResponse; + extractArchiveDialogMask?: string[]; // Remote download dialog remoteDownloadDialogOpen?: boolean; @@ -221,6 +222,7 @@ export interface GlobalStateSlice { epubViewer?: GeneralViewerState; musicPlayer?: MusicPlayerState; excalidrawViewer?: GeneralViewerState; + archiveViewer?: GeneralViewerState; // Viewer selector viewerSelector?: ViewerSelectorState; @@ -375,13 +377,19 @@ export const globalStateSlice = createSlice({ state.customViewer = undefined; state.epubViewer = undefined; state.excalidrawViewer = undefined; + state.archiveViewer = undefined; }, - setExtractArchiveDialog: (state, action: PayloadAction<{ open: boolean; file?: FileResponse }>) => { + setExtractArchiveDialog: ( + state, + action: PayloadAction<{ open: boolean; file?: FileResponse; mask?: string[] }>, + ) => { state.extractArchiveDialogOpen = action.payload.open; state.extractArchiveDialogFile = action.payload.file; + state.extractArchiveDialogMask = action.payload.mask; }, closeExtractArchiveDialog: (state) => { state.extractArchiveDialogOpen = false; + state.extractArchiveDialogMask = undefined; }, setCreateArchiveDialog: ( state, @@ -519,6 +527,12 @@ export const globalStateSlice = createSlice({ closeExcalidrawViewer: (state) => { state.excalidrawViewer && (state.excalidrawViewer.open = false); }, + setArchiveViewer: (state, action: PayloadAction) => { + state.archiveViewer = action.payload; + }, + closeArchiveViewer: (state) => { + state.archiveViewer && (state.archiveViewer.open = false); + }, addShareInfo: (state, action: PayloadAction<{ info: Share; id: string }>) => { state.shareInfo[action.payload.id] = action.payload.info; }, @@ -749,6 +763,8 @@ export const globalStateSlice = createSlice({ export default globalStateSlice.reducer; export const { + setArchiveViewer, + closeArchiveViewer, setUploadRawFiles, setMobileDrawerOpen, setDirectLinkDialog, diff --git a/src/redux/siteConfigSlice.ts b/src/redux/siteConfigSlice.ts index 5c3ba85..ddb6170 100644 --- a/src/redux/siteConfigSlice.ts +++ b/src/redux/siteConfigSlice.ts @@ -1,8 +1,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { Viewer, ViewerPlatform } from "../api/explorer.ts"; import { SiteConfig } from "../api/site.ts"; import { ExpandedIconSettings, FileTypeIconSetting } from "../component/FileManager/Explorer/FileTypeIcon.tsx"; +import SessionManager from "../session/index.ts"; +import Boolset from "../util/boolset.ts"; import { ExpandedViewerSetting } from "./thunks/viewer.ts"; -import { Viewer, ViewerPlatform } from "../api/explorer.ts"; declare global { interface Window { @@ -75,6 +77,18 @@ const preProcessors: { return; } + if (viewer.required_group_permission) { + const group = SessionManager.currentUserGroup(); + if (!group) { + return; + } + + const groupBs = new Boolset(group.permission); + if (viewer.required_group_permission.some((p) => !groupBs.enabled(p))) { + return; + } + } + const platform = viewer.platform || ViewerPlatform.all; if (platform !== ViewerPlatform.all && platform !== (isMobile ? ViewerPlatform.mobile : ViewerPlatform.pc)) { return; diff --git a/src/redux/thunks/viewer.ts b/src/redux/thunks/viewer.ts index 4d75bee..a6645f7 100644 --- a/src/redux/thunks/viewer.ts +++ b/src/redux/thunks/viewer.ts @@ -14,6 +14,7 @@ import CrUri, { CrUriPrefix } from "../../util/uri.ts"; import { closeContextMenu, ContextMenuTypes, fileUpdated } from "../fileManagerSlice.ts"; import { closeImageEditor, + setArchiveViewer, setCodeViewer, setCustomViewer, setDrawIOViewer, @@ -52,6 +53,7 @@ export const builtInViewers = { epub: "epub", music: "music", excalidraw: "excalidraw", + archive: "archive", }; export function openViewers( @@ -233,6 +235,15 @@ export function openViewer( }), ); break; + case builtInViewers.archive: + dispatch( + setArchiveViewer({ + open: true, + file, + version: preferredVersion, + }), + ); + break; case builtInViewers.music: { // open image viewer const fm = getState().fileManager[FileManagerIndex.main];