feat: Start working on a new album/tags/image info modal
This commit is contained in:
parent
b49017aafd
commit
c93ddb0900
|
@ -3,7 +3,7 @@ const Util = require('../../utils/Util');
|
|||
|
||||
class filesGET extends Route {
|
||||
constructor() {
|
||||
super('/file/:id', 'get', { adminOnly: true });
|
||||
super('/admin/file/:id', 'get', { adminOnly: true });
|
||||
}
|
||||
|
||||
async run(req, res, db) {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
const Route = require('../../structures/Route');
|
||||
const Util = require('../../utils/Util');
|
||||
|
||||
class fileGET extends Route {
|
||||
constructor() {
|
||||
super('/file/:id', 'get');
|
||||
}
|
||||
|
||||
async run(req, res, db, user) {
|
||||
const { id } = req.params;
|
||||
if (!id) return res.status(400).json({ message: 'Invalid file ID supplied' });
|
||||
|
||||
/*
|
||||
Make sure the file exists
|
||||
*/
|
||||
let file = await db.table('files').where({ id, userId: user.id }).first();
|
||||
if (!file) return res.status(400).json({ message: 'The file doesn\'t exist or doesn\'t belong to the user' });
|
||||
|
||||
file = Util.constructFilePublicLink(file);
|
||||
|
||||
/*
|
||||
Fetch the albums
|
||||
*/
|
||||
const albums = await db.table('albumsFiles')
|
||||
.where('fileId', id)
|
||||
.join('albums', 'albums.id', 'albumsFiles.albumId')
|
||||
.select('albums.id', 'albums.name');
|
||||
|
||||
/*
|
||||
Fetch the tags
|
||||
*/
|
||||
const tags = await db.table('fileTags')
|
||||
.where('fileId', id)
|
||||
.join('tags', 'tags.id', 'fileTags.id')
|
||||
.select('tags.id', 'tags.uuid', 'tags.name');
|
||||
|
||||
return res.json({
|
||||
message: 'Successfully retrieved file',
|
||||
file,
|
||||
albums,
|
||||
tags,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = fileGET;
|
|
@ -75,7 +75,7 @@
|
|||
</a>
|
||||
</b-tooltip>
|
||||
<b-tooltip label="Albums" position="is-top">
|
||||
<a class="btn" @click="openAlbumModal(item)">
|
||||
<a class="btn" @click="handleFileModal(item)">
|
||||
<i class="icon-interface-window" />
|
||||
</a>
|
||||
</b-tooltip>
|
||||
|
@ -121,7 +121,7 @@
|
|||
|
||||
<b-table-column field="purge" centered>
|
||||
<b-tooltip label="Albums" position="is-top">
|
||||
<a class="btn" @click="openAlbumModal(props.row)">
|
||||
<a class="btn" @click="handleFileModal(props.row)">
|
||||
<i class="icon-interface-window" />
|
||||
</a>
|
||||
</b-tooltip>
|
||||
|
@ -153,7 +153,10 @@
|
|||
</template>
|
||||
</b-table>
|
||||
</div>
|
||||
<b-modal :active.sync="isAlbumsModalActive" :width="640" scroll="keep">
|
||||
<b-modal :active.sync="isAlbumsModalActive" scroll="keep">
|
||||
<ImageInfo :file="modalData.file" />
|
||||
</b-modal>
|
||||
<!-- <b-modal :active.sync="isAlbumsModalActive" :width="640" scroll="keep">
|
||||
<div class="card albumsModal">
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
|
@ -176,7 +179,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</b-modal> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -184,10 +187,12 @@
|
|||
import { mapState } from 'vuex';
|
||||
|
||||
import Waterfall from './waterfall/Waterfall.vue';
|
||||
import ImageInfo from '~/components/image-modal/ImageInfo.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Waterfall,
|
||||
ImageInfo,
|
||||
},
|
||||
props: {
|
||||
files: {
|
||||
|
@ -230,6 +235,11 @@ export default {
|
|||
filesOffsetWaterfall: 0,
|
||||
filesOffsetEndWaterfall: 50,
|
||||
filesPerPageWaterfall: 50,
|
||||
modalData: {
|
||||
file: null,
|
||||
tags: null,
|
||||
albums: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -318,6 +328,20 @@ export default {
|
|||
this.$store.dispatch('alert/set', { text: e.message, error: true }, { root: true });
|
||||
}
|
||||
},
|
||||
async handleFileModal(file) {
|
||||
const { id } = file;
|
||||
|
||||
try {
|
||||
await this.$store.dispatch('images/fetchFileMeta', id);
|
||||
this.modalData.file = this.images.fileExtraInfoMap[id];
|
||||
this.modalData.albums = this.images.fileAlbumsMap[id];
|
||||
this.modalData.tags = this.images.fileTagsMap[id];
|
||||
} catch (e) {
|
||||
this.$store.dispatch('alert/set', { text: e.message, error: true }, { root: true });
|
||||
}
|
||||
|
||||
this.isAlbumsModalActive = true;
|
||||
},
|
||||
mouseOver(id) {
|
||||
const foundIndex = this.hoveredItems.indexOf(id);
|
||||
if (foundIndex > -1) return;
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
<template>
|
||||
<div class="container has-background-lolisafe">
|
||||
<div class="columns is-marginless">
|
||||
<div class="column fucking-opl-shut-up">
|
||||
<img src="https://placehold.it/1024x10024">
|
||||
</div>
|
||||
<div class="column is-one-third">
|
||||
<div class="sticky">
|
||||
<div class="divider is-lolisafe has-text-light">
|
||||
File information
|
||||
</div>
|
||||
<b-field
|
||||
label="ID"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ file.id }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
<b-field
|
||||
label="Name"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ file.name }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="Original Name"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ file.original }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="IP"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ file.ip }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="Link"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<a
|
||||
class="fake-input"
|
||||
:href="file.url"
|
||||
target="_blank">{{ file.url }}</a>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="Size"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ formatBytes(file.size) }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="Hash"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input">{{ file.hash }}</span>
|
||||
</div>
|
||||
</b-field>
|
||||
|
||||
<b-field
|
||||
label="Uploaded"
|
||||
label-position="on-border"
|
||||
type="is-lolisafe"
|
||||
class="lolisafe-on-border">
|
||||
<div class="control">
|
||||
<span class="fake-input"><timeago :since="file.createdAt" /></span>
|
||||
</div>
|
||||
</b-field>
|
||||
<div class="divider is-lolisafe has-text-light">
|
||||
Albums
|
||||
</div>
|
||||
|
||||
<div class="divider is-lolisafe has-text-light">
|
||||
Tags
|
||||
</div>
|
||||
<b-field label="Add some tags">
|
||||
<b-taginput
|
||||
v-model="tags"
|
||||
class="lolisafe"
|
||||
ellipsis
|
||||
icon="label"
|
||||
placeholder="Add a tag" />
|
||||
</b-field>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tags: [],
|
||||
};
|
||||
},
|
||||
computed: mapState(['images']),
|
||||
methods: {
|
||||
formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~/assets/styles/_colors.scss';
|
||||
.modal-content, .modal-card {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.fake-input {
|
||||
font-size: 1rem !important;
|
||||
height: 2.5rem;
|
||||
border-color: #323846; /* $lolisafe */
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
justify-content: flex-start;
|
||||
line-height: 1.5;
|
||||
padding-bottom: calc(0.375em - 1px);
|
||||
padding-left: calc(0.625em - 1px);
|
||||
padding-right: calc(0.625em - 1px);
|
||||
padding-top: calc(0.375em - 1px);
|
||||
background-color: #21252d;
|
||||
border: 2px solid #21252d;
|
||||
border-radius: 0.3em !important;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.divider:first-child {
|
||||
margin: 10px 0 25px;
|
||||
}
|
||||
</style>
|
|
@ -1,4 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import Buefy from 'buefy';
|
||||
// import 'buefy/dist/buefy.css';
|
||||
|
||||
Vue.use(Buefy);
|
||||
|
|
|
@ -34,7 +34,7 @@ export const actions = {
|
|||
return response;
|
||||
},
|
||||
async fetchFile({ commit }, id) {
|
||||
const response = await this.$axios.$get(`file/${id}`);
|
||||
const response = await this.$axios.$get(`admin/file/${id}`);
|
||||
commit('setFile', response);
|
||||
commit('setUserInfo', response);
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@ export const getDefaultState = () => ({
|
|||
limit: 30,
|
||||
totalFiles: 0,
|
||||
},
|
||||
name: null,
|
||||
downloadEnabled: false,
|
||||
albumName: null,
|
||||
albumDownloadEnabled: false,
|
||||
fileExtraInfoMap: {}, // information about the selected file
|
||||
fileAlbumsMap: {}, // map of file ids with a list of album objects the file is in
|
||||
filesTags: {}, // map of file ids with a list of tag objects for the file
|
||||
fileTagsMap: {}, // map of file ids with a list of tag objects for the file
|
||||
});
|
||||
|
||||
export const state = getDefaultState;
|
||||
|
@ -55,6 +56,15 @@ export const actions = {
|
|||
|
||||
return response;
|
||||
},
|
||||
async fetchFileMeta({ commit }, fileId) {
|
||||
const response = await this.$axios.$get(`file/${fileId}`);
|
||||
|
||||
commit('setFileAlbums', { ...response, fileId });
|
||||
commit('setFileTags', { ...response, fileId });
|
||||
commit('setFileExtraInfo', { ...response, fileId });
|
||||
|
||||
return response;
|
||||
},
|
||||
async getFileAlbums({ commit }, fileId) {
|
||||
const response = await this.$axios.$get(`file/${fileId}/albums`);
|
||||
|
||||
|
@ -90,10 +100,11 @@ export const mutations = {
|
|||
state.isLoading = true;
|
||||
},
|
||||
setFilesAndMeta(state, {
|
||||
files, name, page, count,
|
||||
files, name, page, count, downloadEnabled,
|
||||
}) {
|
||||
state.files = files || [];
|
||||
state.name = name ?? null;
|
||||
state.albumName = name ?? null;
|
||||
state.downloadEnabled = downloadEnabled ?? false;
|
||||
state.isLoading = false;
|
||||
state.pagination.page = page || 1;
|
||||
state.pagination.totalFiles = count || 0;
|
||||
|
@ -108,6 +119,12 @@ export const mutations = {
|
|||
setFileAlbums(state, { fileId, albums }) {
|
||||
Vue.set(state.fileAlbumsMap, fileId, albums);
|
||||
},
|
||||
setFileTags(state, { fileId, tags }) {
|
||||
Vue.set(state.fileTagsMap, fileId, tags);
|
||||
},
|
||||
setFileExtraInfo(state, { fileId, file }) {
|
||||
Vue.set(state.fileExtraInfoMap, fileId, file);
|
||||
},
|
||||
addAlbumToFile(state, { fileId, album }) {
|
||||
if (!state.fileAlbumsMap[fileId]) return;
|
||||
|
||||
|
|
Loading…
Reference in New Issue