aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZephyrrus <[email protected]>2020-07-19 22:27:11 +0300
committerZephyrrus <[email protected]>2020-07-19 22:27:11 +0300
commitc93ddb09008c45942544b13bbb03319c367f9cd8 (patch)
tree7f6e334b8d33b42574dc19a256a944fadbaa7f66
parentchore: add custom class to inputs until fix is released on buefy's master (diff)
downloadhost.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.js2
-rw-r--r--src/api/routes/files/fileGET.js46
-rw-r--r--src/site/components/grid/Grid.vue32
-rw-r--r--src/site/components/image-modal/ImageInfo.vue179
-rw-r--r--src/site/components/imageInfo/ImageInfo.vue0
-rw-r--r--src/site/plugins/buefy.js1
-rw-r--r--src/site/store/admin.js2
-rw-r--r--src/site/store/images.js27
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;