diff options
| author | Zephyrrus <[email protected]> | 2020-07-19 22:27:11 +0300 |
|---|---|---|
| committer | Zephyrrus <[email protected]> | 2020-07-19 22:27:11 +0300 |
| commit | c93ddb09008c45942544b13bbb03319c367f9cd8 (patch) | |
| tree | 7f6e334b8d33b42574dc19a256a944fadbaa7f66 | |
| parent | chore: add custom class to inputs until fix is released on buefy's master (diff) | |
| download | host.fuwn.me-c93ddb09008c45942544b13bbb03319c367f9cd8.tar.xz host.fuwn.me-c93ddb09008c45942544b13bbb03319c367f9cd8.zip | |
feat: Start working on a new album/tags/image info modal
| -rw-r--r-- | src/api/routes/admin/fileGET.js | 2 | ||||
| -rw-r--r-- | src/api/routes/files/fileGET.js | 46 | ||||
| -rw-r--r-- | src/site/components/grid/Grid.vue | 32 | ||||
| -rw-r--r-- | src/site/components/image-modal/ImageInfo.vue | 179 | ||||
| -rw-r--r-- | src/site/components/imageInfo/ImageInfo.vue | 0 | ||||
| -rw-r--r-- | src/site/plugins/buefy.js | 1 | ||||
| -rw-r--r-- | src/site/store/admin.js | 2 | ||||
| -rw-r--r-- | src/site/store/images.js | 27 |
8 files changed, 278 insertions, 11 deletions
diff --git a/src/api/routes/admin/fileGET.js b/src/api/routes/admin/fileGET.js index 239b128..7e40659 100644 --- a/src/api/routes/admin/fileGET.js +++ b/src/api/routes/admin/fileGET.js @@ -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) { diff --git a/src/api/routes/files/fileGET.js b/src/api/routes/files/fileGET.js new file mode 100644 index 0000000..e9ce90e --- /dev/null +++ b/src/api/routes/files/fileGET.js @@ -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; diff --git a/src/site/components/grid/Grid.vue b/src/site/components/grid/Grid.vue index 90c196b..1b62097 100644 --- a/src/site/components/grid/Grid.vue +++ b/src/site/components/grid/Grid.vue @@ -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; diff --git a/src/site/components/image-modal/ImageInfo.vue b/src/site/components/image-modal/ImageInfo.vue new file mode 100644 index 0000000..c9dba1a --- /dev/null +++ b/src/site/components/image-modal/ImageInfo.vue @@ -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> diff --git a/src/site/components/imageInfo/ImageInfo.vue b/src/site/components/imageInfo/ImageInfo.vue deleted file mode 100644 index e69de29..0000000 --- a/src/site/components/imageInfo/ImageInfo.vue +++ /dev/null diff --git a/src/site/plugins/buefy.js b/src/site/plugins/buefy.js index 58bf28f..f3f7552 100644 --- a/src/site/plugins/buefy.js +++ b/src/site/plugins/buefy.js @@ -1,4 +1,5 @@ import Vue from 'vue'; import Buefy from 'buefy'; +// import 'buefy/dist/buefy.css'; Vue.use(Buefy); diff --git a/src/site/store/admin.js b/src/site/store/admin.js index b31c446..04ad980 100644 --- a/src/site/store/admin.js +++ b/src/site/store/admin.js @@ -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); diff --git a/src/site/store/images.js b/src/site/store/images.js index acac286..0d5e82a 100644 --- a/src/site/store/images.js +++ b/src/site/store/images.js @@ -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; |