toolshed/frontend/src/views/Files.vue
2026-09-06 03:00:52 +02:00

122 lines
No EOL
3.3 KiB
Vue

<template>
<BaseLayout>
<main class="content">
<div class="container-fluid p-0">
<h1 class="h3 mb-3">Files</h1>
<div class="card">
<div class="card-header">
<h5 class="card-title">Images</h5>
</div>
<div class="card-body">
<div class="masonry-grid">
<div class="masonry-item" v-for="file in only_images(files)" :key="file.id">
<deletable-wrapper confirm-message="Delete this file? This cannot be undone." @delete="deleteFile(file)">
<authenticated-image :src="thumbnailPathForHash(file.hash)" :owner="file.owner"
class="img-preview-masonry"/>
</deletable-wrapper>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5 class="card-title">Other files</h5>
</div>
<div class="card-body">
<ul>
<li v-for="file in whithout_images(files)" :key="file.id">
{{ file.name }}
</li>
</ul>
</div>
</div>
</div>
</main>
</BaseLayout>
</template>
<script>
import {mapActions, mapState} from "vuex";
import * as BIcons from "bootstrap-icons-vue";
import BaseLayout from "@/components/BaseLayout.vue";
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
import DeletableWrapper from "@/components/DeletableWrapper.vue";
export default {
name: "Files",
components: {
AuthenticatedImage,
BaseLayout,
DeletableWrapper,
...BIcons
},
computed: {
...mapState(["files"])
},
methods: {
...mapActions(["fetchFiles", "deleteFile"]),
only_images(files) {
return files.filter(file => file.mime_type.startsWith("image/"));
},
whithout_images(files) {
return files.filter(file => !file.mime_type.startsWith("image/"));
},
// See docs/implementation.md#thumbnail-lookup-by-hash.
thumbnailPathForHash(hash, size = 256) {
return `/media/${size}/${hash.slice(0, 2)}/${hash.slice(2, 4)}/${hash.slice(4, 6)}/${hash.slice(6)}/`;
}
},
mounted() {
this.fetchFiles();
}
}
</script>
<style scoped lang="scss">
@import "../scss/breakpoints";
.img-preview-masonry {
width: 100%;
height: auto;
object-fit: cover;
}
.masonry-item :deep(span) {
display: block !important;
width: 100%;
}
.masonry-grid {
column-count: 2;
column-gap: 1rem;
}
.masonry-item {
break-inside: avoid;
margin-bottom: 1rem;
}
@include media-breakpoint-up(md) {
.masonry-grid {
column-count: 3;
}
}
@include media-breakpoint-up(lg) {
.masonry-grid {
column-count: 4;
}
}
@include media-breakpoint-up(xl) {
.masonry-grid {
column-count: 6;
}
}
@include media-breakpoint-up(xxl) {
.masonry-grid {
column-count: 12;
}
}
</style>