From f7ac3b03ccfa623bacbc5e3f3166124d0d9af280 Mon Sep 17 00:00:00 2001 From: Maxime GRANDCOLAS Date: Fri, 31 Jan 2025 14:56:58 +0100 Subject: [PATCH] [MS] Open files in history with viewers --- client/src/locales/en-US.json | 2 + client/src/locales/fr-FR.json | 2 + client/src/services/fileOpener.ts | 65 +++++++++++++++---- client/src/views/viewers/FileViewer.vue | 19 ++++-- .../views/workspaces/WorkspaceHistoryPage.vue | 14 +--- newsfragments/9482.feature.rst | 1 + 6 files changed, 72 insertions(+), 31 deletions(-) create mode 100644 newsfragments/9482.feature.rst diff --git a/client/src/locales/en-US.json b/client/src/locales/en-US.json index ac96486f32e..ab3ba61ac75 100644 --- a/client/src/locales/en-US.json +++ b/client/src/locales/en-US.json @@ -1758,6 +1758,8 @@ "details": "Details", "openingFile": "Opening file...", "fileTooBig": "File is too big to be opened using a viewer.", + "unknownFileType": "Cannot preview this file type.", + "noFolderPreview": "Cannot preview folders.", "genericError": "Failed to open the file.", "controls": { "zoom": { diff --git a/client/src/locales/fr-FR.json b/client/src/locales/fr-FR.json index 5230db0005b..e9d8027f31f 100644 --- a/client/src/locales/fr-FR.json +++ b/client/src/locales/fr-FR.json @@ -1758,6 +1758,8 @@ "details": "Détails", "openingFile": "Ouverture du fichier...", "fileTooBig": "Le fichier est trop large pour être ouvert avec un visualiseur.", + "unknownFileType": "Impossible de prévisualiser ce type de fichier.", + "noFolderPreview": "Impossible de prévisualiser les dossiers.", "genericError": "Impossible d'ouvrir le fichier.", "controls": { "zoom": { diff --git a/client/src/services/fileOpener.ts b/client/src/services/fileOpener.ts index 1844aef5a2e..df006e13d4c 100644 --- a/client/src/services/fileOpener.ts +++ b/client/src/services/fileOpener.ts @@ -10,6 +10,7 @@ import { Base64, openSpinnerModal } from 'megashark-lib'; interface OpenPathOptions { skipViewers?: boolean; + onlyViewers?: boolean; atTime?: DateTime; } @@ -97,11 +98,31 @@ async function openPath( const entry = statsResult.value; if (!entry.isFile()) { - await openWithSystem(workspaceHandle, entry, informationManager); + if (!options.onlyViewers) { + await openWithSystem(workspaceHandle, entry, informationManager); + } else { + await informationManager.present( + new Information({ + message: 'FoldersPage.open.noFolderPreview', + level: InformationLevel.Error, + }), + PresentationMode.Modal, + ); + } return; } if (isDesktop() && options.skipViewers) { - await openWithSystem(workspaceHandle, entry, informationManager); + if (!options.onlyViewers) { + await openWithSystem(workspaceHandle, entry, informationManager); + } else { + await informationManager.present( + new Information({ + message: 'FoldersPage.open.unknownFileType', + level: InformationLevel.Error, + }), + PresentationMode.Modal, + ); + } return; } @@ -114,7 +135,9 @@ async function openPath( try { if (!contentType || contentType.type === FileContentType.Unknown || (isDesktop() && !ENABLED_FILE_VIEWERS.includes(contentType.type))) { - await openWithSystem(workspaceHandle, entry, informationManager); + if (!options.onlyViewers) { + await openWithSystem(workspaceHandle, entry, informationManager); + } } else { if ((entry as any).size > OPEN_FILE_SIZE_LIMIT) { informationManager.present( @@ -124,20 +147,36 @@ async function openPath( }), PresentationMode.Toast, ); - await openWithSystem(workspaceHandle, entry, informationManager); + if (!options.onlyViewers) { + await openWithSystem(workspaceHandle, entry, informationManager); + } else { + await informationManager.present( + new Information({ + message: 'FoldersPage.open.unknownType', + level: InformationLevel.Error, + }), + PresentationMode.Modal, + ); + } return; } - - recentDocumentManager.addFile({ - entryId: entry.id, - path: entry.path, - workspaceHandle: workspaceHandle, - name: entry.name, - contentType: contentType, - }); + if (!options.atTime) { + recentDocumentManager.addFile({ + entryId: entry.id, + path: entry.path, + workspaceHandle: workspaceHandle, + name: entry.name, + contentType: contentType, + }); + } await navigateTo(Routes.Viewer, { - query: { workspaceHandle: workspaceHandle, documentPath: entry.path, fileTypeInfo: Base64.fromObject(contentType) }, + query: { + workspaceHandle: workspaceHandle, + documentPath: entry.path, + timestamp: options.atTime?.toMillis().toString(), + fileTypeInfo: Base64.fromObject(contentType), + }, }); } } finally { diff --git a/client/src/views/viewers/FileViewer.vue b/client/src/views/viewers/FileViewer.vue index 0ea93cbc0a6..fac26abd541 100644 --- a/client/src/views/viewers/FileViewer.vue +++ b/client/src/views/viewers/FileViewer.vue @@ -19,6 +19,12 @@ {{ contentInfo.fileName }} + + {{ $msTranslate(I18n.formatDate(atDateTime)) }} + = shallowRef(null); const contentInfo: Ref = ref(null); const detectedFileType = ref(null); const loaded = ref(false); -const atDateTime: Ref = ref(null); +const atDateTime: Ref = ref(undefined); const cancelRouteWatch = watchRoute(async () => { if (!currentRouteIs(Routes.Viewer)) { return; } + const query = getCurrentRouteQuery(); + const timestamp = Number.isNaN(Number(query.timestamp)) ? undefined : Number(query.timestamp); + // Same file, no need to reload - if (contentInfo.value && contentInfo.value.path === getDocumentPath()) { + if (contentInfo.value && contentInfo.value.path === getDocumentPath() && atDateTime.value?.toMillis() === timestamp) { return; } await loadFile(); @@ -104,7 +113,7 @@ async function loadFile(): Promise { loaded.value = false; contentInfo.value = null; detectedFileType.value = null; - atDateTime.value = null; + atDateTime.value = undefined; viewerComponent.value = null; const workspaceHandle = getWorkspaceHandle(); if (!workspaceHandle) { @@ -121,7 +130,7 @@ async function loadFile(): Promise { } const timestamp = Number(getCurrentRouteQuery().timestamp); - if (timestamp) { + if (!Number.isNaN(timestamp)) { atDateTime.value = DateTime.fromMillis(timestamp); } diff --git a/client/src/views/workspaces/WorkspaceHistoryPage.vue b/client/src/views/workspaces/WorkspaceHistoryPage.vue index b9745bcce6c..98ad5105e31 100644 --- a/client/src/views/workspaces/WorkspaceHistoryPage.vue +++ b/client/src/views/workspaces/WorkspaceHistoryPage.vue @@ -147,7 +147,6 @@ import { computed, onBeforeUnmount, onMounted, ref, Ref, inject } from 'vue'; import { FsPath, Path, getWorkspaceInfo, StartedWorkspaceInfo, statFolderChildrenAt, entryStatAt, EntryName } from '@/parsec'; import { MsCheckbox, MsSpinner, MsSearchInput, askQuestion, Answer, MsDatetimePicker, I18n } from 'megashark-lib'; import { DateTime } from 'luxon'; -import { StorageManager, StorageManagerKey } from '@/services/storageManager'; import { RouterPathNode } from '@/components/header/HeaderBreadcrumbs.vue'; import HeaderBreadcrumbs from '@/components/header/HeaderBreadcrumbs.vue'; import { WorkspaceHistoryEntryCollection, WorkspaceHistoryEntryModel, HistoryFileListItem } from '@/components/files'; @@ -159,7 +158,6 @@ import { InformationManager, InformationManagerKey } from '@/services/informatio import { openPath } from '@/services/fileOpener'; const fileOperationManager: FileOperationManager = inject(FileOperationManagerKey)!; -const storageManager: StorageManager = inject(StorageManagerKey)!; const informationManager: InformationManager = inject(InformationManagerKey)!; const workspaceInfo: Ref = ref(null); // Default it to 5 seconds ago to not interfere with the `max` value @@ -324,18 +322,8 @@ async function onEntryClicked(entry: WorkspaceHistoryEntryModel): Promise return; } - // Cannot open files for now - - // Doing it this way to trick the linter - const a = 0; - if (a === 0) { - return; - } - - const config = await storageManager.retrieveConfig(); - await openPath(workspaceInfo.value.handle, entry.path, informationManager, { - skipViewers: config.skipViewers, + onlyViewers: true, atTime: DateTime.fromJSDate(selectedDateTime.value), }); } else { diff --git a/newsfragments/9482.feature.rst b/newsfragments/9482.feature.rst new file mode 100644 index 00000000000..261432d5c69 --- /dev/null +++ b/newsfragments/9482.feature.rst @@ -0,0 +1 @@ +Preview some file types when exploring a workspace history