Skip to content

Commit 4e0f062

Browse files
jewhartonStorj Robot
authored andcommitted
web/satellite/vuetify-poc: support PDF previewing
This change allows supported platforms to preview PDFs in the object browser. Resolves #6397 Change-Id: I6078914f0ddf5f620514e27e51d24ba8c11a2786
1 parent 7038dde commit 4e0f062

File tree

2 files changed

+73
-67
lines changed

2 files changed

+73
-67
lines changed

web/satellite/vuetify-poc/src/components/dialogs/filePreviewComponents/FilePreviewItem.vue

Lines changed: 41 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
<v-container v-if="isLoading" class="fill-height flex-column justify-center align-center mt-n16">
66
<v-progress-circular indeterminate />
77
</v-container>
8-
<v-container v-else-if="previewIsVideo" class="fill-height flex-column justify-center align-center">
8+
<v-container v-else-if="previewType === PreviewType.Video" class="fill-height flex-column justify-center align-center">
99
<video
1010
controls
1111
:src="objectPreviewUrl"
1212
style="max-width: 100%; max-height: 90%;"
1313
aria-roledescription="video-preview"
1414
/>
1515
</v-container>
16-
<v-container v-else-if="previewIsAudio" class="fill-height flex-column justify-center align-center">
16+
<v-container v-else-if="previewType === PreviewType.Audio" class="fill-height flex-column justify-center align-center">
1717
<audio
1818
controls
1919
:src="objectPreviewUrl"
2020
aria-roledescription="audio-preview"
2121
/>
2222
</v-container>
23-
<v-container v-else-if="previewIsImage" class="fill-height flex-column justify-center align-center">
23+
<v-container v-else-if="previewType === PreviewType.Image" class="fill-height flex-column justify-center align-center">
2424
<img
2525
v-if="objectPreviewUrl"
2626
:src="objectPreviewUrl"
@@ -29,54 +29,62 @@
2929
alt="preview"
3030
>
3131
</v-container>
32-
<v-container v-else-if="placeHolderDisplayable || previewAndMapFailed" class="fill-height flex-column justify-center align-center mt-n16">
33-
<p class="mb-5">{{ file?.Key || '' }}</p>
34-
<p class="text-h5 mb-5 font-weight-bold">No preview available</p>
35-
<v-btn
36-
@click="() => emits('download')"
32+
<v-container v-else-if="previewType === PreviewType.PDF" class="fill-height flex-column justify-center align-center">
33+
<object
34+
:data="objectPreviewUrl"
35+
type="application/pdf"
36+
aria-roledescription="pdf-preview"
37+
class="h-100 w-100"
3738
>
38-
<template #prepend>
39-
<img src="@poc/assets/icon-download.svg" width="22" alt="Download">
40-
</template>
41-
{{ `Download (${prettyBytes(file?.Size || 0)})` }}
42-
</v-btn>
39+
<file-preview-placeholder :file="file" @download="emit('download')" />
40+
</object>
4341
</v-container>
42+
<file-preview-placeholder v-else class="mt-n16" :file="file" @download="emit('download')" />
4443
</template>
4544

4645
<script setup lang="ts">
4746
import { computed, ref, watch } from 'vue';
48-
import { VBtn, VContainer, VProgressCircular } from 'vuetify/components';
49-
import { useRoute } from 'vue-router';
50-
import prettyBytes from 'pretty-bytes';
47+
import { VContainer, VProgressCircular } from 'vuetify/components';
5148
52-
import { useAppStore } from '@/store/modules/appStore';
5349
import { BrowserObject, PreviewCache, useObjectBrowserStore } from '@/store/modules/objectBrowserStore';
5450
import { useBucketsStore } from '@/store/modules/bucketsStore';
5551
import { useNotify } from '@/utils/hooks';
5652
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
5753
import { useLinksharing } from '@/composables/useLinksharing';
5854
59-
const appStore = useAppStore();
55+
import FilePreviewPlaceholder from '@poc/components/dialogs/filePreviewComponents/FilePreviewPlaceholder.vue';
56+
57+
enum PreviewType {
58+
None,
59+
Image,
60+
Video,
61+
Audio,
62+
PDF,
63+
}
64+
6065
const obStore = useObjectBrowserStore();
6166
const bucketsStore = useBucketsStore();
6267
const notify = useNotify();
6368
const { generateObjectPreviewAndMapURL } = useLinksharing();
6469
65-
const route = useRoute();
66-
6770
const isLoading = ref<boolean>(false);
6871
const previewAndMapFailed = ref<boolean>(false);
6972
70-
const imgExts = ['bmp', 'svg', 'jpg', 'jpeg', 'png', 'ico', 'gif'];
71-
const videoExts = ['m4v', 'mp4', 'webm', 'mov', 'mkv'];
72-
const audioExts = ['m4a', 'mp3', 'wav', 'ogg'];
73+
const extInfos = new Map<string[], PreviewType>([
74+
[['bmp', 'svg', 'jpg', 'jpeg', 'png', 'ico', 'gif'], PreviewType.Image],
75+
[['m4v', 'mp4', 'webm', 'mov', 'mkv'], PreviewType.Video],
76+
[['m4a', 'mp3', 'wav', 'ogg'], PreviewType.Audio],
77+
[['pdf'], PreviewType.PDF],
78+
]);
7379
7480
const props = defineProps<{
7581
file: BrowserObject,
7682
active: boolean, // whether this item is visible
7783
}>();
7884
79-
const emits = defineEmits(['download']);
85+
const emit = defineEmits<{
86+
download: [];
87+
}>();
8088
8189
/**
8290
* Returns object preview URLs cache from store.
@@ -109,54 +117,20 @@ const encodedFilePath = computed((): string => {
109117
});
110118
111119
/**
112-
* Get the extension of the current file.
113-
*/
114-
const extension = computed((): string | undefined => {
115-
return props.file.Key.split('.').pop();
116-
});
117-
118-
/**
119-
* Check to see if the current file is an image file.
120+
* Returns the type of object being previewed.
120121
*/
121-
const previewIsImage = computed((): boolean => {
122-
if (!extension.value) {
123-
return false;
124-
}
122+
const previewType = computed<PreviewType>(() => {
123+
if (previewAndMapFailed.value) return PreviewType.None;
125124
126-
return imgExts.includes(extension.value.toLowerCase());
127-
});
125+
const dotIdx = props.file.Key.lastIndexOf('.');
126+
if (dotIdx === -1) return PreviewType.None;
128127
129-
/**
130-
* Check to see if the current file is a video file.
131-
*/
132-
const previewIsVideo = computed((): boolean => {
133-
if (!extension.value) {
134-
return false;
128+
const ext = props.file.Key.toLowerCase().slice(dotIdx + 1);
129+
for (const [exts, previewType] of extInfos) {
130+
if (exts.includes(ext)) return previewType;
135131
}
136132
137-
return videoExts.includes(extension.value.toLowerCase());
138-
});
139-
140-
/**
141-
* Check to see if the current file is an audio file.
142-
*/
143-
const previewIsAudio = computed((): boolean => {
144-
if (!extension.value) {
145-
return false;
146-
}
147-
148-
return audioExts.includes(extension.value.toLowerCase());
149-
});
150-
151-
/**
152-
* Check to see if the current file is neither an image file, video file, or audio file.
153-
*/
154-
const placeHolderDisplayable = computed((): boolean => {
155-
return [
156-
previewIsImage.value,
157-
previewIsVideo.value,
158-
previewIsAudio.value,
159-
].every((value) => !value);
133+
return PreviewType.None;
160134
});
161135
162136
/**
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (C) 2023 Storj Labs, Inc.
2+
// See LICENSE for copying information.
3+
4+
<template>
5+
<div class="h-100 w-100 d-flex flex-column align-center justify-center">
6+
<p class="mb-5">{{ file?.Key || '' }}</p>
7+
<p class="text-h5 mb-5 font-weight-bold">No preview available</p>
8+
<v-btn
9+
@click="() => emits('download')"
10+
>
11+
<template #prepend>
12+
<img src="@poc/assets/icon-download.svg" width="22" alt="Download">
13+
</template>
14+
{{ `Download (${prettyBytes(file?.Size || 0)})` }}
15+
</v-btn>
16+
</div>
17+
</template>
18+
19+
<script setup lang="ts">
20+
import { VBtn } from 'vuetify/components';
21+
import prettyBytes from 'pretty-bytes';
22+
23+
import { BrowserObject } from '@/store/modules/objectBrowserStore';
24+
25+
const props = defineProps<{
26+
file: BrowserObject,
27+
}>();
28+
29+
const emits = defineEmits<{
30+
download: [];
31+
}>();
32+
</script>

0 commit comments

Comments
 (0)