mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-25 19:52:48 +00:00
feat: preview archive file and extract selected files
This commit is contained in:
parent
64b34b280f
commit
463794a71e
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Google Docs Online-Reader",
|
"googledocs": "Google Docs Online-Reader",
|
||||||
"m365viewer": "Microsoft Office Online-Reader",
|
"m365viewer": "Microsoft Office Online-Reader",
|
||||||
"pdfViewer": "PDF-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äß.",
|
"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",
|
"testSubtitleStyle": "Untertitel-Stil testen AaBbCc",
|
||||||
"color": "Farbe",
|
"color": "Farbe",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Google Docs Viewer",
|
"googledocs": "Google Docs Viewer",
|
||||||
"m365viewer": "Microsoft Office Online Viewer",
|
"m365viewer": "Microsoft Office Online Viewer",
|
||||||
"pdfViewer": "PDF 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.",
|
"viewerFileSizeWarning": "Size of opened file ({{file_size}}) exceed limit ({{max}}) of {{app}}, it might not work properly.",
|
||||||
"testSubtitleStyle": "Test subtitle style AaBbCc",
|
"testSubtitleStyle": "Test subtitle style AaBbCc",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Visor de Google Docs",
|
"googledocs": "Visor de Google Docs",
|
||||||
"m365viewer": "Visor en línea de Microsoft Office",
|
"m365viewer": "Visor en línea de Microsoft Office",
|
||||||
"pdfViewer": "Visor PDF",
|
"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.",
|
"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",
|
"testSubtitleStyle": "Probar estilo de subtítulos AaBbCc",
|
||||||
"color": "Color",
|
"color": "Color",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Visionneuse Google Docs",
|
"googledocs": "Visionneuse Google Docs",
|
||||||
"m365viewer": "Visionneuse Microsoft Office Online",
|
"m365viewer": "Visionneuse Microsoft Office Online",
|
||||||
"pdfViewer": "Visionneuse PDF",
|
"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.",
|
"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",
|
"testSubtitleStyle": "Tester le style de sous-titres AaBbCc",
|
||||||
"color": "Couleur",
|
"color": "Couleur",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Visualizzatore Google Docs",
|
"googledocs": "Visualizzatore Google Docs",
|
||||||
"m365viewer": "Visualizzatore Microsoft Office Online",
|
"m365viewer": "Visualizzatore Microsoft Office Online",
|
||||||
"pdfViewer": "Visualizzatore PDF",
|
"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.",
|
"viewerFileSizeWarning": "La dimensione del file aperto ({{file_size}}) supera il limite ({{max}}) di {{app}}, potrebbe non funzionare correttamente.",
|
||||||
"testSubtitleStyle": "Testa stile sottotitoli AaBbCc",
|
"testSubtitleStyle": "Testa stile sottotitoli AaBbCc",
|
||||||
"color": "Colore",
|
"color": "Colore",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Googleドキュメント オンラインリーダー",
|
"googledocs": "Googleドキュメント オンラインリーダー",
|
||||||
"m365viewer": "Microsoft Office オンラインリーダー",
|
"m365viewer": "Microsoft Office オンラインリーダー",
|
||||||
"pdfViewer": "PDFリーダー",
|
"pdfViewer": "PDFリーダー",
|
||||||
|
"archivePreview": "アーカイブプレビュー",
|
||||||
|
"extractSelected": "選択したファイルを展開",
|
||||||
"viewerFileSizeWarning": "ファイルサイズ({{file_size}})が{{app}}の制限({{max}})を超えているため、正常に動作しない可能性があります。",
|
"viewerFileSizeWarning": "ファイルサイズ({{file_size}})が{{app}}の制限({{max}})を超えているため、正常に動作しない可能性があります。",
|
||||||
"testSubtitleStyle": "字幕スタイルのテスト AaBbCc",
|
"testSubtitleStyle": "字幕スタイルのテスト AaBbCc",
|
||||||
"color": "色",
|
"color": "色",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Google Docs 온라인 뷰어",
|
"googledocs": "Google Docs 온라인 뷰어",
|
||||||
"m365viewer": "Microsoft Office 온라인 뷰어",
|
"m365viewer": "Microsoft Office 온라인 뷰어",
|
||||||
"pdfViewer": "PDF 뷰어",
|
"pdfViewer": "PDF 뷰어",
|
||||||
|
"archivePreview": "아카이브 미리보기",
|
||||||
|
"extractSelected": "선택한 파일 압축 해제",
|
||||||
"viewerFileSizeWarning": "열린 파일 크기({{file_size}})가 {{app}}의 제한({{max}})을 초과하여 정상적으로 작동하지 않을 수 있습니다.",
|
"viewerFileSizeWarning": "열린 파일 크기({{file_size}})가 {{app}}의 제한({{max}})을 초과하여 정상적으로 작동하지 않을 수 있습니다.",
|
||||||
"testSubtitleStyle": "자막 스타일 테스트 AaBbCc",
|
"testSubtitleStyle": "자막 스타일 테스트 AaBbCc",
|
||||||
"color": "색상",
|
"color": "색상",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Visualizador Google Docs",
|
"googledocs": "Visualizador Google Docs",
|
||||||
"m365viewer": "Visualizador Microsoft Office Online",
|
"m365viewer": "Visualizador Microsoft Office Online",
|
||||||
"pdfViewer": "Visualizador PDF",
|
"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.",
|
"viewerFileSizeWarning": "Tamanho do arquivo aberto ({{file_size}}) excede o limite ({{max}}) do {{app}}, pode não funcionar corretamente.",
|
||||||
"testSubtitleStyle": "Testar estilo de legenda AaBbCc",
|
"testSubtitleStyle": "Testar estilo de legenda AaBbCc",
|
||||||
"color": "Cor",
|
"color": "Cor",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Онлайн-просмотрщик Google Docs",
|
"googledocs": "Онлайн-просмотрщик Google Docs",
|
||||||
"m365viewer": "Онлайн-просмотрщик Microsoft Office",
|
"m365viewer": "Онлайн-просмотрщик Microsoft Office",
|
||||||
"pdfViewer": "Просмотрщик PDF",
|
"pdfViewer": "Просмотрщик PDF",
|
||||||
|
"archivePreview": "Предварительный просмотр архива",
|
||||||
|
"extractSelected": "Извлечь выбранные файлы",
|
||||||
"viewerFileSizeWarning": "Размер открываемого файла ({{file_size}}) превышает лимит {{app}} ({{max}}), возможно, он не будет работать корректно.",
|
"viewerFileSizeWarning": "Размер открываемого файла ({{file_size}}) превышает лимит {{app}} ({{max}}), возможно, он не будет работать корректно.",
|
||||||
"testSubtitleStyle": "Тест стиля субтитров AaBbCc",
|
"testSubtitleStyle": "Тест стиля субтитров AaBbCc",
|
||||||
"color": "Цвет",
|
"color": "Цвет",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Google Docs 在线阅读器",
|
"googledocs": "Google Docs 在线阅读器",
|
||||||
"m365viewer": "Microsoft Office 在线阅读器",
|
"m365viewer": "Microsoft Office 在线阅读器",
|
||||||
"pdfViewer": "PDF 阅读器",
|
"pdfViewer": "PDF 阅读器",
|
||||||
|
"archivePreview": "压缩包预览",
|
||||||
|
"extractSelected": "解压缩选中的文件",
|
||||||
"viewerFileSizeWarning": "打开的文件大小 ({{file_size}}) 超过了 {{app}} 的限制 ({{max}}),可能无法正常工作。",
|
"viewerFileSizeWarning": "打开的文件大小 ({{file_size}}) 超过了 {{app}} 的限制 ({{max}}),可能无法正常工作。",
|
||||||
"testSubtitleStyle": "测试字幕样式 AaBbCc",
|
"testSubtitleStyle": "测试字幕样式 AaBbCc",
|
||||||
"color": "颜色",
|
"color": "颜色",
|
||||||
|
|
|
||||||
|
|
@ -1116,7 +1116,7 @@
|
||||||
"allowWabDAVDes": "关闭后,用户无法通过 WebDAV 协议连接至网盘。",
|
"allowWabDAVDes": "关闭后,用户无法通过 WebDAV 协议连接至网盘。",
|
||||||
"allowWabDAVProxy": "WebDAV 代理",
|
"allowWabDAVProxy": "WebDAV 代理",
|
||||||
"allowWabDAVProxyDes": "启用后,用户可以配置 WebDAV 下载经由 Cloudreve 中转。",
|
"allowWabDAVProxyDes": "启用后,用户可以配置 WebDAV 下载经由 Cloudreve 中转。",
|
||||||
"allowCompressTask": "压缩/解压缩任务",
|
"compressTask": "压缩/解压缩任务",
|
||||||
"compressTaskDes": "开启后,用户可以在线压缩/解压缩文件。",
|
"compressTaskDes": "开启后,用户可以在线压缩/解压缩文件。",
|
||||||
"compressSize": "待压缩文件最大大小",
|
"compressSize": "待压缩文件最大大小",
|
||||||
"compressSizeDes": "用户可创建的压缩任务的文件最大总大小,填写为 0 表示不限制。这一限制在创建压缩任务时不会检查,当执行时已处理原始文件总大小超过此限制时,任务会失败。",
|
"compressSizeDes": "用户可创建的压缩任务的文件最大总大小,填写为 0 表示不限制。这一限制在创建压缩任务时不会检查,当执行时已处理原始文件总大小超过此限制时,任务会失败。",
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@
|
||||||
"googledocs": "Google Docs 線上閱讀器",
|
"googledocs": "Google Docs 線上閱讀器",
|
||||||
"m365viewer": "Microsoft Office 線上閱讀器",
|
"m365viewer": "Microsoft Office 線上閱讀器",
|
||||||
"pdfViewer": "PDF 閱讀器",
|
"pdfViewer": "PDF 閱讀器",
|
||||||
|
"archivePreview": "壓縮包預覽",
|
||||||
|
"extractSelected": "解壓縮選中的檔案",
|
||||||
"viewerFileSizeWarning": "開啟的檔案大小 ({{file_size}}) 超過了 {{app}} 的限制 ({{max}}),可能無法正常工作。",
|
"viewerFileSizeWarning": "開啟的檔案大小 ({{file_size}}) 超過了 {{app}} 的限制 ({{max}}),可能無法正常工作。",
|
||||||
"testSubtitleStyle": "測試字幕樣式 AaBbCc",
|
"testSubtitleStyle": "測試字幕樣式 AaBbCc",
|
||||||
"color": "顏色",
|
"color": "顏色",
|
||||||
|
|
|
||||||
|
|
@ -1116,7 +1116,7 @@
|
||||||
"allowWabDAVDes": "關閉後,使用者無法通過 WebDAV 協議連線至網盤。",
|
"allowWabDAVDes": "關閉後,使用者無法通過 WebDAV 協議連線至網盤。",
|
||||||
"allowWabDAVProxy": "WebDAV 代理",
|
"allowWabDAVProxy": "WebDAV 代理",
|
||||||
"allowWabDAVProxyDes": "啟用後, 使用者可以配置 WebDAV 下載經由 Cloudreve 中轉。",
|
"allowWabDAVProxyDes": "啟用後, 使用者可以配置 WebDAV 下載經由 Cloudreve 中轉。",
|
||||||
"allowCompressTask": "壓縮/解壓縮任務",
|
"compressTask": "壓縮/解壓縮任務",
|
||||||
"compressTaskDes": "開啟後,使用者可以線上壓縮/解壓縮檔案。",
|
"compressTaskDes": "開啟後,使用者可以線上壓縮/解壓縮檔案。",
|
||||||
"compressSize": "待壓縮檔案最大大小",
|
"compressSize": "待壓縮檔案最大大小",
|
||||||
"compressSizeDes": "使用者可建立的壓縮任務的檔案最大總大小,填寫為 0 表示不限制。這一限制在建立壓縮任務時不會檢查,當執行時已處理原始檔案總大小超過此限制時,任務會失敗。",
|
"compressSizeDes": "使用者可建立的壓縮任務的檔案最大總大小,填寫為 0 表示不限制。這一限制在建立壓縮任務時不會檢查,當執行時已處理原始檔案總大小超過此限制時,任務會失敗。",
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ import {
|
||||||
User as UserEnt,
|
User as UserEnt,
|
||||||
} from "./dashboard.ts";
|
} from "./dashboard.ts";
|
||||||
import {
|
import {
|
||||||
|
ArchiveListFilesResponse,
|
||||||
|
ArchiveListFilesService,
|
||||||
CreateFileService,
|
CreateFileService,
|
||||||
CreateViewerSessionService,
|
CreateViewerSessionService,
|
||||||
DeleteFileService,
|
DeleteFileService,
|
||||||
|
|
@ -2007,3 +2009,17 @@ export function sendCleanupTask(args: CleanupTaskService): ThunkResponse<void> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getArchiveListFiles(args: ArchiveListFilesService): ThunkResponse<ArchiveListFilesResponse> {
|
||||||
|
return async (dispatch, _getState) => {
|
||||||
|
return await dispatch(
|
||||||
|
send(
|
||||||
|
`/file/archive`,
|
||||||
|
{ method: "GET", params: args },
|
||||||
|
{
|
||||||
|
...defaultOpts,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -447,6 +447,7 @@ export interface Viewer {
|
||||||
};
|
};
|
||||||
templates?: NewFileTemplate[];
|
templates?: NewFileTemplate[];
|
||||||
platform?: ViewerPlatform;
|
platform?: ViewerPlatform;
|
||||||
|
required_group_permission?: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewFileTemplate {
|
export interface NewFileTemplate {
|
||||||
|
|
@ -548,3 +549,19 @@ export enum CustomPropsType {
|
||||||
link = "link",
|
link = "link",
|
||||||
rating = "rating",
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export interface ArchiveWorkflowService {
|
||||||
dst: string;
|
dst: string;
|
||||||
encoding?: string;
|
encoding?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
|
file_mask?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TaskListResponse {
|
export interface TaskListResponse {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import SaveAs from "./SaveAs.tsx";
|
||||||
import Photopea from "../../Viewers/Photopea/Photopea.tsx";
|
import Photopea from "../../Viewers/Photopea/Photopea.tsx";
|
||||||
import OpenWith from "./OpenWith.tsx";
|
import OpenWith from "./OpenWith.tsx";
|
||||||
import Wopi from "../../Viewers/Wopi.tsx";
|
import Wopi from "../../Viewers/Wopi.tsx";
|
||||||
|
import ArchivePreview from "../../Viewers/ArchivePreview/ArchivePreview.tsx";
|
||||||
import CodeViewer from "../../Viewers/CodeViewer/CodeViewer.tsx";
|
import CodeViewer from "../../Viewers/CodeViewer/CodeViewer.tsx";
|
||||||
import DrawIOViewer from "../../Viewers/DrawIO/DrawIOViewer.tsx";
|
import DrawIOViewer from "../../Viewers/DrawIO/DrawIOViewer.tsx";
|
||||||
import MarkdownViewer from "../../Viewers/MarkdownEditor/MarkdownViewer.tsx";
|
import MarkdownViewer from "../../Viewers/MarkdownEditor/MarkdownViewer.tsx";
|
||||||
|
|
@ -41,6 +42,7 @@ const Dialogs = () => {
|
||||||
const directLink = useAppSelector((state) => state.globalState.directLinkDialogOpen);
|
const directLink = useAppSelector((state) => state.globalState.directLinkDialogOpen);
|
||||||
const excalidrawViewer = useAppSelector((state) => state.globalState.excalidrawViewer);
|
const excalidrawViewer = useAppSelector((state) => state.globalState.excalidrawViewer);
|
||||||
const directLinkManagement = useAppSelector((state) => state.globalState.directLinkManagementDialogOpen);
|
const directLinkManagement = useAppSelector((state) => state.globalState.directLinkManagementDialogOpen);
|
||||||
|
const archivePreview = useAppSelector((state) => state.globalState.archiveViewer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -75,6 +77,7 @@ const Dialogs = () => {
|
||||||
{directLink != undefined && <DirectLinks />}
|
{directLink != undefined && <DirectLinks />}
|
||||||
{excalidrawViewer != undefined && <ExcalidrawViewer />}
|
{excalidrawViewer != undefined && <ExcalidrawViewer />}
|
||||||
{directLinkManagement != undefined && <DirectLinksControl />}
|
{directLinkManagement != undefined && <DirectLinksControl />}
|
||||||
|
{archivePreview != undefined && <ArchivePreview />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ const ExtractArchive = () => {
|
||||||
const open = useAppSelector((state) => state.globalState.extractArchiveDialogOpen);
|
const open = useAppSelector((state) => state.globalState.extractArchiveDialogOpen);
|
||||||
const target = useAppSelector((state) => state.globalState.extractArchiveDialogFile);
|
const target = useAppSelector((state) => state.globalState.extractArchiveDialogFile);
|
||||||
const current = useAppSelector((state) => state.fileManager[FileManagerIndex.main].pure_path);
|
const current = useAppSelector((state) => state.fileManager[FileManagerIndex.main].pure_path);
|
||||||
|
const mask = useAppSelector((state) => state.globalState.extractArchiveDialogMask);
|
||||||
|
|
||||||
const showEncodingOption = useMemo(() => {
|
const showEncodingOption = useMemo(() => {
|
||||||
const ext = fileExtension(target?.name ?? "");
|
const ext = fileExtension(target?.name ?? "");
|
||||||
|
|
@ -116,6 +117,7 @@ const ExtractArchive = () => {
|
||||||
dst: path,
|
dst: path,
|
||||||
encoding: showEncodingOption && encoding != defaultEncodingValue ? encoding : undefined,
|
encoding: showEncodingOption && encoding != defaultEncodingValue ? encoding : undefined,
|
||||||
password: showPasswordOption && password ? password : undefined,
|
password: showPasswordOption && password ? password : undefined,
|
||||||
|
file_mask: mask ?? undefined,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -129,7 +131,7 @@ const ExtractArchive = () => {
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, [target, encoding, path, showPasswordOption, showEncodingOption, password]);
|
}, [target, encoding, path, showPasswordOption, showEncodingOption, password, mask]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableDialog
|
<DraggableDialog
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { SecondaryButton } from "../../Common/StyledComponents.tsx";
|
||||||
import DraggableDialog, { StyledDialogContentText } from "../../Dialogs/DraggableDialog.tsx";
|
import DraggableDialog, { StyledDialogContentText } from "../../Dialogs/DraggableDialog.tsx";
|
||||||
import Book from "../../Icons/Book.tsx";
|
import Book from "../../Icons/Book.tsx";
|
||||||
import DocumentPDF from "../../Icons/DocumentPDF.tsx";
|
import DocumentPDF from "../../Icons/DocumentPDF.tsx";
|
||||||
|
import FolderZip from "../../Icons/FolderZip.tsx";
|
||||||
import Image from "../../Icons/Image.tsx";
|
import Image from "../../Icons/Image.tsx";
|
||||||
import Markdown from "../../Icons/Markdown.tsx";
|
import Markdown from "../../Icons/Markdown.tsx";
|
||||||
import MoreHorizontal from "../../Icons/MoreHorizontal.tsx";
|
import MoreHorizontal from "../../Icons/MoreHorizontal.tsx";
|
||||||
|
|
@ -44,6 +45,7 @@ export const ViewerIDWithDefaultIcons = [
|
||||||
builtInViewers.epub,
|
builtInViewers.epub,
|
||||||
builtInViewers.music,
|
builtInViewers.music,
|
||||||
builtInViewers.markdown,
|
builtInViewers.markdown,
|
||||||
|
builtInViewers.archive,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ViewerIcon = ({ viewer, size = 32, py = 0.5 }: ViewerIconProps) => {
|
export const ViewerIcon = ({ viewer, size = 32, py = 0.5 }: ViewerIconProps) => {
|
||||||
|
|
@ -62,6 +64,8 @@ export const ViewerIcon = ({ viewer, size = 32, py = 0.5 }: ViewerIconProps) =>
|
||||||
return <Book sx={{ width: size, height: size, color: "#81b315" }} />;
|
return <Book sx={{ width: size, height: size, color: "#81b315" }} />;
|
||||||
case builtInViewers.music:
|
case builtInViewers.music:
|
||||||
return <MusicNote1 sx={{ width: size, height: size, color: "#651fff" }} />;
|
return <MusicNote1 sx={{ width: size, height: size, color: "#651fff" }} />;
|
||||||
|
case builtInViewers.archive:
|
||||||
|
return <FolderZip sx={{ width: size, height: size, color: "#f9a825" }} />;
|
||||||
case builtInViewers.markdown:
|
case builtInViewers.markdown:
|
||||||
return (
|
return (
|
||||||
<Markdown
|
<Markdown
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,372 @@
|
||||||
|
import { Box, Breadcrumbs, Button, Link, Table, TableCell, TableContainer, Typography, useTheme } from "@mui/material";
|
||||||
|
import { useSnackbar } from "notistack";
|
||||||
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { TableVirtuoso } from "react-virtuoso";
|
||||||
|
import { getArchiveListFiles } from "../../../api/api.ts";
|
||||||
|
import { ArchivedFile, FileType } from "../../../api/explorer.ts";
|
||||||
|
import { closeArchiveViewer, setExtractArchiveDialog } from "../../../redux/globalStateSlice.ts";
|
||||||
|
import { useAppDispatch, useAppSelector } from "../../../redux/hooks.ts";
|
||||||
|
import { fileBase, getFileLinkedUri, sizeToString } from "../../../util";
|
||||||
|
import { SecondaryButton, StyledCheckbox, StyledTableContainerPaper } from "../../Common/StyledComponents.tsx";
|
||||||
|
import TimeBadge from "../../Common/TimeBadge.tsx";
|
||||||
|
import FileIcon from "../../FileManager/Explorer/FileIcon.tsx";
|
||||||
|
import ChevronRight from "../../Icons/ChevronRight.tsx";
|
||||||
|
import Folder from "../../Icons/Folder.tsx";
|
||||||
|
import Home from "../../Icons/Home.tsx";
|
||||||
|
import ViewerDialog, { ViewerLoading } from "../ViewerDialog.tsx";
|
||||||
|
|
||||||
|
const ArchivePreview = () => {
|
||||||
|
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<ArchivedFile[]>([]);
|
||||||
|
const [currentPath, setCurrentPath] = useState<string>("");
|
||||||
|
const [selectedFiles, setSelectedFiles] = useState<string[]>([]);
|
||||||
|
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<string>();
|
||||||
|
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 (
|
||||||
|
<>
|
||||||
|
<ViewerDialog
|
||||||
|
file={viewerState?.file}
|
||||||
|
loading={loading}
|
||||||
|
dialogProps={{
|
||||||
|
open: !!(viewerState && viewerState.open),
|
||||||
|
onClose: onClose,
|
||||||
|
fullWidth: true,
|
||||||
|
maxWidth: "lg",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading && <ViewerLoading />}
|
||||||
|
{!loading && (
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
|
<Box sx={{ mb: 2 }}>
|
||||||
|
<Breadcrumbs separator={<ChevronRight fontSize="small" />}>
|
||||||
|
<Link
|
||||||
|
component="button"
|
||||||
|
variant="body2"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => navigateToBreadcrumb(-1)}
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
textDecoration: "none",
|
||||||
|
"&:hover": { textDecoration: "underline" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Home fontSize="small" sx={{ mr: 0.5 }} />
|
||||||
|
{t("fileManager.rootFolder")}
|
||||||
|
</Link>
|
||||||
|
{breadcrumbPaths.map((path, index) => {
|
||||||
|
const isLast = index === breadcrumbPaths.length - 1;
|
||||||
|
return isLast ? (
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
key={index}
|
||||||
|
color="text.primary"
|
||||||
|
sx={{ display: "flex", alignItems: "center" }}
|
||||||
|
>
|
||||||
|
<Folder fontSize="small" sx={{ mr: 0.5 }} />
|
||||||
|
{path}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
key={index}
|
||||||
|
component="button"
|
||||||
|
variant="body2"
|
||||||
|
color="inherit"
|
||||||
|
onClick={() => navigateToBreadcrumb(index)}
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
textDecoration: "none",
|
||||||
|
"&:hover": { textDecoration: "underline" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Folder fontSize="small" sx={{ mr: 0.5 }} />
|
||||||
|
{path}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Breadcrumbs>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{filteredFiles.length > 0 ? (
|
||||||
|
<TableContainer component={StyledTableContainerPaper}>
|
||||||
|
<TableVirtuoso
|
||||||
|
style={{
|
||||||
|
height: Math.min(height, 400),
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
totalListHeightChanged={(h) => {
|
||||||
|
setHeight(h + 0.5);
|
||||||
|
}}
|
||||||
|
components={{
|
||||||
|
// eslint-disable-next-line react/display-name
|
||||||
|
Table: (props) => <Table {...props} size="small" />,
|
||||||
|
}}
|
||||||
|
data={filteredFiles}
|
||||||
|
itemContent={(_index, file) => {
|
||||||
|
const fullPath = currentPath ? currentPath + "/" + file.name : file.name;
|
||||||
|
const isSelected = selectedFiles.includes(fullPath);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TableCell sx={{ width: 50, padding: "4px 8px" }}>
|
||||||
|
<StyledCheckbox
|
||||||
|
checked={isSelected}
|
||||||
|
onChange={() => toggleFileSelection(file.name)}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
sx={{
|
||||||
|
minWidth: 300,
|
||||||
|
width: "100%",
|
||||||
|
padding: "4px 8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" sx={{ display: "flex", alignItems: "center" }}>
|
||||||
|
<FileIcon
|
||||||
|
sx={{ px: 0, py: 0, mr: 1, height: "20px" }}
|
||||||
|
variant="small"
|
||||||
|
iconProps={{ fontSize: "small" }}
|
||||||
|
file={{
|
||||||
|
type: file.is_directory ? FileType.folder : FileType.file,
|
||||||
|
name: file.name,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{file.is_directory ? (
|
||||||
|
<Typography
|
||||||
|
component="button"
|
||||||
|
variant="inherit"
|
||||||
|
onClick={() => 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)}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography
|
||||||
|
variant="inherit"
|
||||||
|
sx={{
|
||||||
|
color: "inherit",
|
||||||
|
fontWeight: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fileBase(file.name)}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ minWidth: 100, padding: "4px 8px" }}>
|
||||||
|
<Typography variant="body2" noWrap>
|
||||||
|
{file.is_directory ? "-" : sizeToString(file.size)}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ minWidth: 120, padding: "4px 8px" }}>
|
||||||
|
<Typography variant="body2" noWrap>
|
||||||
|
{file.updated_at ? <TimeBadge variant="inherit" datetime={file.updated_at} /> : "-"}
|
||||||
|
</Typography>
|
||||||
|
</TableCell>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</TableContainer>
|
||||||
|
) : (
|
||||||
|
<Typography variant="body2" color="text.secondary" align="center" sx={{ py: 4 }}>
|
||||||
|
{t("fileManager.nothingFound")}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!viewerState?.version && (
|
||||||
|
<Box sx={{ mt: 2, display: "flex", flexWrap: "wrap", gap: 1 }}>
|
||||||
|
<Button variant="contained" onClick={extractArchive} color="primary">
|
||||||
|
{t("fileManager.extractArchive")}
|
||||||
|
</Button>
|
||||||
|
{selectedFiles.length > 0 && (
|
||||||
|
<SecondaryButton variant={"contained"} onClick={extractSelectedFiles}>
|
||||||
|
{t("fileManager.extractSelected")}
|
||||||
|
</SecondaryButton>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</ViewerDialog>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ArchivePreview;
|
||||||
|
|
@ -178,6 +178,7 @@ export interface GlobalStateSlice {
|
||||||
// Extract archive dialog
|
// Extract archive dialog
|
||||||
extractArchiveDialogOpen?: boolean;
|
extractArchiveDialogOpen?: boolean;
|
||||||
extractArchiveDialogFile?: FileResponse;
|
extractArchiveDialogFile?: FileResponse;
|
||||||
|
extractArchiveDialogMask?: string[];
|
||||||
|
|
||||||
// Remote download dialog
|
// Remote download dialog
|
||||||
remoteDownloadDialogOpen?: boolean;
|
remoteDownloadDialogOpen?: boolean;
|
||||||
|
|
@ -221,6 +222,7 @@ export interface GlobalStateSlice {
|
||||||
epubViewer?: GeneralViewerState;
|
epubViewer?: GeneralViewerState;
|
||||||
musicPlayer?: MusicPlayerState;
|
musicPlayer?: MusicPlayerState;
|
||||||
excalidrawViewer?: GeneralViewerState;
|
excalidrawViewer?: GeneralViewerState;
|
||||||
|
archiveViewer?: GeneralViewerState;
|
||||||
|
|
||||||
// Viewer selector
|
// Viewer selector
|
||||||
viewerSelector?: ViewerSelectorState;
|
viewerSelector?: ViewerSelectorState;
|
||||||
|
|
@ -375,13 +377,19 @@ export const globalStateSlice = createSlice({
|
||||||
state.customViewer = undefined;
|
state.customViewer = undefined;
|
||||||
state.epubViewer = undefined;
|
state.epubViewer = undefined;
|
||||||
state.excalidrawViewer = 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.extractArchiveDialogOpen = action.payload.open;
|
||||||
state.extractArchiveDialogFile = action.payload.file;
|
state.extractArchiveDialogFile = action.payload.file;
|
||||||
|
state.extractArchiveDialogMask = action.payload.mask;
|
||||||
},
|
},
|
||||||
closeExtractArchiveDialog: (state) => {
|
closeExtractArchiveDialog: (state) => {
|
||||||
state.extractArchiveDialogOpen = false;
|
state.extractArchiveDialogOpen = false;
|
||||||
|
state.extractArchiveDialogMask = undefined;
|
||||||
},
|
},
|
||||||
setCreateArchiveDialog: (
|
setCreateArchiveDialog: (
|
||||||
state,
|
state,
|
||||||
|
|
@ -519,6 +527,12 @@ export const globalStateSlice = createSlice({
|
||||||
closeExcalidrawViewer: (state) => {
|
closeExcalidrawViewer: (state) => {
|
||||||
state.excalidrawViewer && (state.excalidrawViewer.open = false);
|
state.excalidrawViewer && (state.excalidrawViewer.open = false);
|
||||||
},
|
},
|
||||||
|
setArchiveViewer: (state, action: PayloadAction<GeneralViewerState>) => {
|
||||||
|
state.archiveViewer = action.payload;
|
||||||
|
},
|
||||||
|
closeArchiveViewer: (state) => {
|
||||||
|
state.archiveViewer && (state.archiveViewer.open = false);
|
||||||
|
},
|
||||||
addShareInfo: (state, action: PayloadAction<{ info: Share; id: string }>) => {
|
addShareInfo: (state, action: PayloadAction<{ info: Share; id: string }>) => {
|
||||||
state.shareInfo[action.payload.id] = action.payload.info;
|
state.shareInfo[action.payload.id] = action.payload.info;
|
||||||
},
|
},
|
||||||
|
|
@ -749,6 +763,8 @@ export const globalStateSlice = createSlice({
|
||||||
|
|
||||||
export default globalStateSlice.reducer;
|
export default globalStateSlice.reducer;
|
||||||
export const {
|
export const {
|
||||||
|
setArchiveViewer,
|
||||||
|
closeArchiveViewer,
|
||||||
setUploadRawFiles,
|
setUploadRawFiles,
|
||||||
setMobileDrawerOpen,
|
setMobileDrawerOpen,
|
||||||
setDirectLinkDialog,
|
setDirectLinkDialog,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
import { Viewer, ViewerPlatform } from "../api/explorer.ts";
|
||||||
import { SiteConfig } from "../api/site.ts";
|
import { SiteConfig } from "../api/site.ts";
|
||||||
import { ExpandedIconSettings, FileTypeIconSetting } from "../component/FileManager/Explorer/FileTypeIcon.tsx";
|
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 { ExpandedViewerSetting } from "./thunks/viewer.ts";
|
||||||
import { Viewer, ViewerPlatform } from "../api/explorer.ts";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
|
@ -75,6 +77,18 @@ const preProcessors: {
|
||||||
return;
|
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;
|
const platform = viewer.platform || ViewerPlatform.all;
|
||||||
if (platform !== ViewerPlatform.all && platform !== (isMobile ? ViewerPlatform.mobile : ViewerPlatform.pc)) {
|
if (platform !== ViewerPlatform.all && platform !== (isMobile ? ViewerPlatform.mobile : ViewerPlatform.pc)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import CrUri, { CrUriPrefix } from "../../util/uri.ts";
|
||||||
import { closeContextMenu, ContextMenuTypes, fileUpdated } from "../fileManagerSlice.ts";
|
import { closeContextMenu, ContextMenuTypes, fileUpdated } from "../fileManagerSlice.ts";
|
||||||
import {
|
import {
|
||||||
closeImageEditor,
|
closeImageEditor,
|
||||||
|
setArchiveViewer,
|
||||||
setCodeViewer,
|
setCodeViewer,
|
||||||
setCustomViewer,
|
setCustomViewer,
|
||||||
setDrawIOViewer,
|
setDrawIOViewer,
|
||||||
|
|
@ -52,6 +53,7 @@ export const builtInViewers = {
|
||||||
epub: "epub",
|
epub: "epub",
|
||||||
music: "music",
|
music: "music",
|
||||||
excalidraw: "excalidraw",
|
excalidraw: "excalidraw",
|
||||||
|
archive: "archive",
|
||||||
};
|
};
|
||||||
|
|
||||||
export function openViewers(
|
export function openViewers(
|
||||||
|
|
@ -233,6 +235,15 @@ export function openViewer(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case builtInViewers.archive:
|
||||||
|
dispatch(
|
||||||
|
setArchiveViewer({
|
||||||
|
open: true,
|
||||||
|
file,
|
||||||
|
version: preferredVersion,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
break;
|
||||||
case builtInViewers.music: {
|
case builtInViewers.music: {
|
||||||
// open image viewer
|
// open image viewer
|
||||||
const fm = getState().fileManager[FileManagerIndex.main];
|
const fm = getState().fileManager[FileManagerIndex.main];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue