converted files/Preview.vue to composition api & ts

This commit is contained in:
Joep 2023-09-19 22:29:12 +02:00
parent 66423fc98f
commit 5d8f4863ca
No known key found for this signature in database
GPG Key ID: 6F5588F1DC2A8209
2 changed files with 207 additions and 217 deletions

View File

@ -18,13 +18,13 @@ interface Resource extends ResourceBase {
sorting: Sorting; sorting: Sorting;
hash?: string; hash?: string;
token?: string; token?: string;
}
interface ResourceItem extends ResourceBase {
index?: number; index?: number;
subtitles?: string[]; subtitles?: string[];
} }
// interface ResourceItem extends ResourceBase {
// }
type ResourceType = type ResourceType =
| "video" | "video"
| "audio" | "audio"

View File

@ -8,37 +8,37 @@
<action icon="close" :label="$t('buttons.close')" @action="close()" /> <action icon="close" :label="$t('buttons.close')" @action="close()" />
<title>{{ name }}</title> <title>{{ name }}</title>
<action <action
:disabled="loading" :disabled="layoutStore.loading"
v-if="isResizeEnabled && req.type === 'image'" v-if="isResizeEnabled && fileStore.req?.type === 'image'"
:icon="fullSize ? 'photo_size_select_large' : 'hd'" :icon="fullSize ? 'photo_size_select_large' : 'hd'"
@action="toggleSize" @action="toggleSize"
/> />
<template #actions> <template #actions>
<action <action
:disabled="loading" :disabled="layoutStore.loading"
v-if="user.perm.rename" v-if="authStore.user?.perm.rename"
icon="mode_edit" icon="mode_edit"
:label="$t('buttons.rename')" :label="$t('buttons.rename')"
show="rename" show="rename"
/> />
<action <action
:disabled="loading" :disabled="layoutStore.loading"
v-if="user.perm.delete" v-if="authStore.user?.perm.delete"
icon="delete" icon="delete"
:label="$t('buttons.delete')" :label="$t('buttons.delete')"
@action="deleteFile" @action="deleteFile"
id="delete-button" id="delete-button"
/> />
<action <action
:disabled="loading" :disabled="layoutStore.loading"
v-if="user.perm.download" v-if="authStore.user?.perm.download"
icon="file_download" icon="file_download"
:label="$t('buttons.download')" :label="$t('buttons.download')"
@action="download" @action="download"
/> />
<action <action
:disabled="loading" :disabled="layoutStore.loading"
icon="info" icon="info"
:label="$t('buttons.info')" :label="$t('buttons.info')"
show="info" show="info"
@ -46,7 +46,7 @@
</template> </template>
</header-bar> </header-bar>
<div class="loading delayed" v-if="loading"> <div class="loading delayed" v-if="layoutStore.loading">
<div class="spinner"> <div class="spinner">
<div class="bounce1"></div> <div class="bounce1"></div>
<div class="bounce2"></div> <div class="bounce2"></div>
@ -55,9 +55,12 @@
</div> </div>
<template v-else> <template v-else>
<div class="preview"> <div class="preview">
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage> <ExtendedImage
v-if="fileStore.req?.type == 'image'"
:src="raw"
></ExtendedImage>
<audio <audio
v-else-if="req.type == 'audio'" v-else-if="fileStore.req?.type == 'audio'"
ref="player" ref="player"
:src="raw" :src="raw"
controls controls
@ -65,7 +68,7 @@
@play="autoPlay = true" @play="autoPlay = true"
></audio> ></audio>
<video <video
v-else-if="req.type == 'video'" v-else-if="fileStore.req?.type == 'video'"
ref="player" ref="player"
:src="raw" :src="raw"
controls controls
@ -85,11 +88,11 @@
and watch it with your favorite video player! and watch it with your favorite video player!
</video> </video>
<object <object
v-else-if="req.extension.toLowerCase() == '.pdf'" v-else-if="fileStore.req?.extension.toLowerCase() == '.pdf'"
class="pdf" class="pdf"
:data="raw" :data="raw"
></object> ></object>
<div v-else-if="req.type == 'blob'" class="info"> <div v-else-if="fileStore.req?.type == 'blob'" class="info">
<div class="title"> <div class="title">
<i class="material-icons">feedback</i> <i class="material-icons">feedback</i>
{{ $t("files.noPreview") }} {{ $t("files.noPreview") }}
@ -105,7 +108,7 @@
target="_blank" target="_blank"
:href="raw" :href="raw"
class="button button--flat" class="button button--flat"
v-if="!req.isDir" v-if="!fileStore.req?.isDir"
> >
<div> <div>
<i class="material-icons">open_in_new</i <i class="material-icons">open_in_new</i
@ -142,8 +145,7 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapActions, mapState } from "pinia";
import { useAuthStore } from "@/stores/auth"; import { useAuthStore } from "@/stores/auth";
import { useFileStore } from "@/stores/file"; import { useFileStore } from "@/stores/file";
import { useLayoutStore } from "@/stores/layout"; import { useLayoutStore } from "@/stores/layout";
@ -155,210 +157,198 @@ import throttle from "lodash/throttle";
import HeaderBar from "@/components/header/HeaderBar.vue"; import HeaderBar from "@/components/header/HeaderBar.vue";
import Action from "@/components/header/Action.vue"; import Action from "@/components/header/Action.vue";
import ExtendedImage from "@/components/files/ExtendedImage.vue"; import ExtendedImage from "@/components/files/ExtendedImage.vue";
import { computed, inject, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
const mediaTypes = ["image", "video", "audio", "blob"]; const mediaTypes: ResourceType[] = ["image", "video", "audio", "blob"];
export default { const previousLink = ref<string>("");
name: "preview", const nextLink = ref<string>("");
components: { const listing = ref<null | Resource[]>(null);
HeaderBar, const name = ref<string>("");
Action, const fullSize = ref<boolean>(false);
ExtendedImage, const showNav = ref<boolean>(true);
}, const navTimeout = ref<null | number>(null);
data: function () { const hoverNav = ref<boolean>(false);
return { const autoPlay = ref<boolean>(false);
previousLink: "", const previousRaw = ref<string>("");
nextLink: "", const nextRaw = ref<string>("");
listing: null,
name: "",
fullSize: false,
showNav: true,
navTimeout: null,
hoverNav: false,
autoPlay: false,
previousRaw: "",
nextRaw: "",
};
},
inject: ["$showError"],
computed: {
...mapState(useAuthStore, ["user", "jwt"]),
...mapState(useFileStore, ["req", "oldReq", "loading"]),
...mapState(useLayoutStore, ["show"]),
hasPrevious() {
return this.previousLink !== "";
},
hasNext() {
return this.nextLink !== "";
},
downloadUrl() {
return api.getDownloadURL(this.req);
},
raw() {
console.log(this.req);
if (this.req.type === "image" && !this.fullSize) {
return api.getPreviewURL(this.req, "big");
}
return api.getDownloadURL(this.req, true); const player = ref<HTMLVideoElement | HTMLAudioElement | null>(null);
},
showMore() {
return this.show === "more";
},
isResizeEnabled() {
return resizePreview;
},
subtitles() {
if (this.req.subtitles) {
return api.getSubtitlesURL(this.req);
}
return [];
},
},
watch: {
$route: function () {
this.updatePreview();
this.toggleNavigation();
},
},
async mounted() {
window.addEventListener("keydown", this.key);
this.listing = this.oldReq.items;
this.updatePreview();
},
beforeUnmount() {
window.removeEventListener("keydown", this.key);
},
methods: {
...mapActions(useFileStore, ["updateRequest"]),
...mapActions(useLayoutStore, ["showHover", "closeHovers"]),
deleteFile() {
this.showHover({
prompt: "delete",
confirm: () => {
this.listing = this.listing.filter((item) => item.name !== this.name);
if (this.hasNext) { const $showError = inject<IToastError>("$showError")!;
this.next();
} else if (!this.hasPrevious && !this.hasNext) { const authStore = useAuthStore();
this.close(); const fileStore = useFileStore();
} else { const layoutStore = useLayoutStore();
this.prev();
} const route = useRoute();
}, const router = useRouter();
});
}, const hasPrevious = computed(() => previousLink.value !== "");
prev() {
this.hoverNav = false; const hasNext = computed(() => nextLink.value !== "");
this.$router.replace({ path: this.previousLink });
}, const downloadUrl = computed(() =>
next() { fileStore.req ? api.getDownloadURL(fileStore.req, true) : ""
this.hoverNav = false; );
this.$router.replace({ path: this.nextLink });
}, const raw = computed(() => {
key(event) { if (fileStore.req?.type === "image" && !fullSize.value) {
if (this.show !== null) { return api.getPreviewURL(fileStore.req, "big");
}
return downloadUrl.value;
});
const isResizeEnabled = computed(() => resizePreview);
const subtitles = computed(() => {
if (fileStore.req?.subtitles) {
return api.getSubtitlesURL(fileStore.req);
}
return [];
});
watch(route, () => {
updatePreview();
toggleNavigation();
});
// Specify hooks
onMounted(async () => {
window.addEventListener("keydown", key);
if (fileStore.oldReq) {
listing.value = fileStore.oldReq.items;
updatePreview();
}
});
onBeforeUnmount(() => window.removeEventListener("keydown", key));
// Specify methods
const deleteFile = () => {
layoutStore.showHover({
prompt: "delete",
confirm: () => {
if (listing.value === null) {
return; return;
} }
listing.value = listing.value.filter((item) => item.name !== name.value);
if (event.which === 13 || event.which === 39) { if (hasNext.value) {
// right arrow next();
if (this.hasNext) this.next(); } else if (!hasPrevious.value && !hasNext.value) {
} else if (event.which === 37) { close();
// left arrow } else {
if (this.hasPrevious) this.prev(); prev();
} else if (event.which === 27) {
// esc
this.close();
} }
}, },
async updatePreview() { });
if (
this.$refs.player &&
this.$refs.player.paused &&
!this.$refs.player.ended
) {
this.autoPlay = false;
}
let dirs = this.$route.fullPath.split("/");
this.name = decodeURIComponent(dirs[dirs.length - 1]);
if (!this.listing) {
try {
const path = url.removeLastDir(this.$route.path);
const res = await api.fetch(path);
this.listing = res.items;
} catch (e) {
this.$showError(e);
}
}
this.previousLink = "";
this.nextLink = "";
for (let i = 0; i < this.listing.length; i++) {
if (this.listing[i].name !== this.name) {
continue;
}
for (let j = i - 1; j >= 0; j--) {
if (mediaTypes.includes(this.listing[j].type)) {
this.previousLink = this.listing[j].url;
this.previousRaw = this.prefetchUrl(this.listing[j]);
break;
}
}
for (let j = i + 1; j < this.listing.length; j++) {
if (mediaTypes.includes(this.listing[j].type)) {
this.nextLink = this.listing[j].url;
this.nextRaw = this.prefetchUrl(this.listing[j]);
break;
}
}
return;
}
},
prefetchUrl(item) {
if (item.type !== "image") {
return "";
}
return this.fullSize
? api.getDownloadURL(item, true)
: api.getPreviewURL(item, "big");
},
openMore() {
this.showHover("more");
},
resetPrompts() {
this.closeHovers();
},
toggleSize() {
this.fullSize = !this.fullSize;
},
toggleNavigation: throttle(function () {
this.showNav = true;
if (this.navTimeout) {
clearTimeout(this.navTimeout);
}
this.navTimeout = setTimeout(() => {
this.showNav = false || this.hoverNav;
this.navTimeout = null;
}, 1500);
}, 500),
close() {
this.updateRequest({});
let uri = url.removeLastDir(this.$route.path) + "/";
this.$router.push({ path: uri });
},
download() {
window.open(this.downloadUrl);
},
},
}; };
const prev = () => {
hoverNav.value = false;
router.replace({ path: previousLink.value });
};
const next = () => {
hoverNav.value = false;
router.replace({ path: nextLink.value });
};
const key = (event: KeyboardEvent) => {
if (layoutStore.show !== null) {
return;
}
if (event.which === 13 || event.which === 39) {
// right arrow
if (hasNext.value) next();
} else if (event.which === 37) {
// left arrow
if (hasPrevious.value) prev();
} else if (event.which === 27) {
// esc
close();
}
};
const updatePreview = async () => {
if (player.value && player.value.paused && !player.value.ended) {
autoPlay.value = false;
}
let dirs = route.fullPath.split("/");
name.value = decodeURIComponent(dirs[dirs.length - 1]);
if (!listing.value) {
try {
const path = url.removeLastDir(route.path);
const res = await api.fetch(path);
listing.value = res.items;
} catch (e: any) {
$showError(e);
}
}
previousLink.value = "";
nextLink.value = "";
if (listing.value) {
for (let i = 0; i < listing.value.length; i++) {
if (listing.value[i].name !== name.value) {
continue;
}
for (let j = i - 1; j >= 0; j--) {
if (mediaTypes.includes(listing.value[j].type)) {
previousLink.value = listing.value[j].url;
previousRaw.value = prefetchUrl(listing.value[j]);
break;
}
}
for (let j = i + 1; j < listing.value.length; j++) {
if (mediaTypes.includes(listing.value[j].type)) {
nextLink.value = listing.value[j].url;
nextRaw.value = prefetchUrl(listing.value[j]);
break;
}
}
return;
}
}
};
const prefetchUrl = (item: Resource) => {
if (item.type !== "image") {
return "";
}
return fullSize.value
? api.getDownloadURL(item, true)
: api.getPreviewURL(item, "big");
};
const toggleSize = () => (fullSize.value = !fullSize.value);
const toggleNavigation = throttle(function () {
showNav.value = true;
if (navTimeout.value) {
clearTimeout(navTimeout.value);
}
navTimeout.value = setTimeout(() => {
showNav.value = false || hoverNav.value;
navTimeout.value = null;
}, 1500);
}, 500);
const close = () => {
fileStore.updateRequest(null);
let uri = url.removeLastDir(route.path) + "/";
router.push({ path: uri });
};
const download = () => window.open(downloadUrl.value);
</script> </script>