web/satellite/vuetify-poc: support PDF previewing
This change allows supported platforms to preview PDFs in the object browser. Resolves #6397 Change-Id: I6078914f0ddf5f620514e27e51d24ba8c11a2786
This commit is contained in:
parent
7038ddef7f
commit
4e0f062cb5
@ -5,7 +5,7 @@
|
|||||||
<v-container v-if="isLoading" class="fill-height flex-column justify-center align-center mt-n16">
|
<v-container v-if="isLoading" class="fill-height flex-column justify-center align-center mt-n16">
|
||||||
<v-progress-circular indeterminate />
|
<v-progress-circular indeterminate />
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container v-else-if="previewIsVideo" class="fill-height flex-column justify-center align-center">
|
<v-container v-else-if="previewType === PreviewType.Video" class="fill-height flex-column justify-center align-center">
|
||||||
<video
|
<video
|
||||||
controls
|
controls
|
||||||
:src="objectPreviewUrl"
|
:src="objectPreviewUrl"
|
||||||
@ -13,14 +13,14 @@
|
|||||||
aria-roledescription="video-preview"
|
aria-roledescription="video-preview"
|
||||||
/>
|
/>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container v-else-if="previewIsAudio" class="fill-height flex-column justify-center align-center">
|
<v-container v-else-if="previewType === PreviewType.Audio" class="fill-height flex-column justify-center align-center">
|
||||||
<audio
|
<audio
|
||||||
controls
|
controls
|
||||||
:src="objectPreviewUrl"
|
:src="objectPreviewUrl"
|
||||||
aria-roledescription="audio-preview"
|
aria-roledescription="audio-preview"
|
||||||
/>
|
/>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container v-else-if="previewIsImage" class="fill-height flex-column justify-center align-center">
|
<v-container v-else-if="previewType === PreviewType.Image" class="fill-height flex-column justify-center align-center">
|
||||||
<img
|
<img
|
||||||
v-if="objectPreviewUrl"
|
v-if="objectPreviewUrl"
|
||||||
:src="objectPreviewUrl"
|
:src="objectPreviewUrl"
|
||||||
@ -29,54 +29,62 @@
|
|||||||
alt="preview"
|
alt="preview"
|
||||||
>
|
>
|
||||||
</v-container>
|
</v-container>
|
||||||
<v-container v-else-if="placeHolderDisplayable || previewAndMapFailed" class="fill-height flex-column justify-center align-center mt-n16">
|
<v-container v-else-if="previewType === PreviewType.PDF" class="fill-height flex-column justify-center align-center">
|
||||||
<p class="mb-5">{{ file?.Key || '' }}</p>
|
<object
|
||||||
<p class="text-h5 mb-5 font-weight-bold">No preview available</p>
|
:data="objectPreviewUrl"
|
||||||
<v-btn
|
type="application/pdf"
|
||||||
@click="() => emits('download')"
|
aria-roledescription="pdf-preview"
|
||||||
|
class="h-100 w-100"
|
||||||
>
|
>
|
||||||
<template #prepend>
|
<file-preview-placeholder :file="file" @download="emit('download')" />
|
||||||
<img src="@poc/assets/icon-download.svg" width="22" alt="Download">
|
</object>
|
||||||
</template>
|
|
||||||
{{ `Download (${prettyBytes(file?.Size || 0)})` }}
|
|
||||||
</v-btn>
|
|
||||||
</v-container>
|
</v-container>
|
||||||
|
<file-preview-placeholder v-else class="mt-n16" :file="file" @download="emit('download')" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { VBtn, VContainer, VProgressCircular } from 'vuetify/components';
|
import { VContainer, VProgressCircular } from 'vuetify/components';
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import prettyBytes from 'pretty-bytes';
|
|
||||||
|
|
||||||
import { useAppStore } from '@/store/modules/appStore';
|
|
||||||
import { BrowserObject, PreviewCache, useObjectBrowserStore } from '@/store/modules/objectBrowserStore';
|
import { BrowserObject, PreviewCache, useObjectBrowserStore } from '@/store/modules/objectBrowserStore';
|
||||||
import { useBucketsStore } from '@/store/modules/bucketsStore';
|
import { useBucketsStore } from '@/store/modules/bucketsStore';
|
||||||
import { useNotify } from '@/utils/hooks';
|
import { useNotify } from '@/utils/hooks';
|
||||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||||
import { useLinksharing } from '@/composables/useLinksharing';
|
import { useLinksharing } from '@/composables/useLinksharing';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
import FilePreviewPlaceholder from '@poc/components/dialogs/filePreviewComponents/FilePreviewPlaceholder.vue';
|
||||||
|
|
||||||
|
enum PreviewType {
|
||||||
|
None,
|
||||||
|
Image,
|
||||||
|
Video,
|
||||||
|
Audio,
|
||||||
|
PDF,
|
||||||
|
}
|
||||||
|
|
||||||
const obStore = useObjectBrowserStore();
|
const obStore = useObjectBrowserStore();
|
||||||
const bucketsStore = useBucketsStore();
|
const bucketsStore = useBucketsStore();
|
||||||
const notify = useNotify();
|
const notify = useNotify();
|
||||||
const { generateObjectPreviewAndMapURL } = useLinksharing();
|
const { generateObjectPreviewAndMapURL } = useLinksharing();
|
||||||
|
|
||||||
const route = useRoute();
|
|
||||||
|
|
||||||
const isLoading = ref<boolean>(false);
|
const isLoading = ref<boolean>(false);
|
||||||
const previewAndMapFailed = ref<boolean>(false);
|
const previewAndMapFailed = ref<boolean>(false);
|
||||||
|
|
||||||
const imgExts = ['bmp', 'svg', 'jpg', 'jpeg', 'png', 'ico', 'gif'];
|
const extInfos = new Map<string[], PreviewType>([
|
||||||
const videoExts = ['m4v', 'mp4', 'webm', 'mov', 'mkv'];
|
[['bmp', 'svg', 'jpg', 'jpeg', 'png', 'ico', 'gif'], PreviewType.Image],
|
||||||
const audioExts = ['m4a', 'mp3', 'wav', 'ogg'];
|
[['m4v', 'mp4', 'webm', 'mov', 'mkv'], PreviewType.Video],
|
||||||
|
[['m4a', 'mp3', 'wav', 'ogg'], PreviewType.Audio],
|
||||||
|
[['pdf'], PreviewType.PDF],
|
||||||
|
]);
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
file: BrowserObject,
|
file: BrowserObject,
|
||||||
active: boolean, // whether this item is visible
|
active: boolean, // whether this item is visible
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emits = defineEmits(['download']);
|
const emit = defineEmits<{
|
||||||
|
download: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns object preview URLs cache from store.
|
* Returns object preview URLs cache from store.
|
||||||
@ -109,54 +117,20 @@ const encodedFilePath = computed((): string => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the extension of the current file.
|
* Returns the type of object being previewed.
|
||||||
*/
|
*/
|
||||||
const extension = computed((): string | undefined => {
|
const previewType = computed<PreviewType>(() => {
|
||||||
return props.file.Key.split('.').pop();
|
if (previewAndMapFailed.value) return PreviewType.None;
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
const dotIdx = props.file.Key.lastIndexOf('.');
|
||||||
* Check to see if the current file is an image file.
|
if (dotIdx === -1) return PreviewType.None;
|
||||||
*/
|
|
||||||
const previewIsImage = computed((): boolean => {
|
const ext = props.file.Key.toLowerCase().slice(dotIdx + 1);
|
||||||
if (!extension.value) {
|
for (const [exts, previewType] of extInfos) {
|
||||||
return false;
|
if (exts.includes(ext)) return previewType;
|
||||||
}
|
}
|
||||||
|
|
||||||
return imgExts.includes(extension.value.toLowerCase());
|
return PreviewType.None;
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if the current file is a video file.
|
|
||||||
*/
|
|
||||||
const previewIsVideo = computed((): boolean => {
|
|
||||||
if (!extension.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return videoExts.includes(extension.value.toLowerCase());
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if the current file is an audio file.
|
|
||||||
*/
|
|
||||||
const previewIsAudio = computed((): boolean => {
|
|
||||||
if (!extension.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return audioExts.includes(extension.value.toLowerCase());
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if the current file is neither an image file, video file, or audio file.
|
|
||||||
*/
|
|
||||||
const placeHolderDisplayable = computed((): boolean => {
|
|
||||||
return [
|
|
||||||
previewIsImage.value,
|
|
||||||
previewIsVideo.value,
|
|
||||||
previewIsAudio.value,
|
|
||||||
].every((value) => !value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (C) 2023 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="h-100 w-100 d-flex flex-column align-center justify-center">
|
||||||
|
<p class="mb-5">{{ file?.Key || '' }}</p>
|
||||||
|
<p class="text-h5 mb-5 font-weight-bold">No preview available</p>
|
||||||
|
<v-btn
|
||||||
|
@click="() => emits('download')"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<img src="@poc/assets/icon-download.svg" width="22" alt="Download">
|
||||||
|
</template>
|
||||||
|
{{ `Download (${prettyBytes(file?.Size || 0)})` }}
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { VBtn } from 'vuetify/components';
|
||||||
|
import prettyBytes from 'pretty-bytes';
|
||||||
|
|
||||||
|
import { BrowserObject } from '@/store/modules/objectBrowserStore';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
file: BrowserObject,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emits = defineEmits<{
|
||||||
|
download: [];
|
||||||
|
}>();
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user