Pārlūkot izejas kodu

Be able to add a file to multiple albums

Pitu 6 gadi atpakaļ
vecāks
revīzija
ce76a8ec7b

+ 26 - 0
src/api/routes/files/albumAddPOST.js

@@ -0,0 +1,26 @@
+const Route = require('../../structures/Route');
+
+class albumAddPOST extends Route {
+	constructor() {
+		super('/file/album/add', 'post');
+	}
+
+	async run(req, res, db) {
+		if (!req.body) return res.status(400).json({ message: 'No body provided' });
+		const { fileId, albumId } = req.body;
+		if (!fileId || !albumId) return res.status(400).json({ message: 'No id provided' });
+
+		try {
+			await db.table('albumsFiles')
+				.insert({ fileId, albumId });
+		} catch (error) {
+			return super.error(res, error);
+		}
+
+		return res.json({
+			message: 'Successfully added file to album'
+		});
+	}
+}
+
+module.exports = albumAddPOST;

+ 27 - 0
src/api/routes/files/albumDelPOST.js

@@ -0,0 +1,27 @@
+const Route = require('../../structures/Route');
+
+class albumDelPOST extends Route {
+	constructor() {
+		super('/file/album/del', 'post');
+	}
+
+	async run(req, res, db) {
+		if (!req.body) return res.status(400).json({ message: 'No body provided' });
+		const { fileId, albumId } = req.body;
+		if (!fileId || !albumId) return res.status(400).json({ message: 'No id provided' });
+
+		try {
+			await db.table('albumsFiles')
+				.where({ fileId, albumId })
+				.delete();
+		} catch (error) {
+			return super.error(res, error);
+		}
+
+		return res.json({
+			message: 'Successfully removed file from album'
+		});
+	}
+}
+
+module.exports = albumDelPOST;

+ 15 - 0
src/api/routes/files/filesGET.js

@@ -14,6 +14,21 @@ class filesGET extends Route {
 			.where('userId', user.id)
 			.orderBy('id', 'desc');
 
+		for (const file of files) {
+			file.albums = [];
+			const albumFiles = await db.table('albumsFiles')
+				.where('fileId', file.id);
+			if (!albumFiles.length) continue;
+
+			for (const albumFile of albumFiles) {
+				const album = await db.table('albums')
+					.where('id', albumFile.albumId)
+					.select('id', 'name')
+					.first();
+				if (!album) continue;
+				file.albums.push(album);
+			}
+		}
 		/*
 			For each file, create the public link to be able to display the file
 		*/

+ 0 - 4
src/api/routes/files/uploadPOST.js

@@ -6,14 +6,11 @@ const log = require('../../utils/Log');
 const jetpack = require('fs-jetpack');
 const Busboy = require('busboy');
 const fs = require('fs');
-const { dump } = require('dumper.js');
 /*
 	TODO: Strip exif data if the owner/user configured it as such
 	TODO: If source has transparency generate a png thumbnail, otherwise a jpg.
 	TODO: If source is a gif, generate a thumb of the first frame and play the gif on hover.
 	TODO: If source is a video, generate a thumb of the first frame and save the video length.
-	TODO: Check that the async isAuthorized works and is not nulling out
-	TODO: Lowercase the file extensions
 */
 
 class uploadPOST extends Route {
@@ -23,7 +20,6 @@ class uploadPOST extends Route {
 
 	async run(req, res, db) {
 		const user = await Util.isAuthorized(req);
-		// TODO: .env variables are all casted to strings. pepehands
 		if (!user && process.env.PUBLIC_MODE == 'false') return res.status(401).json({ message: 'Not authorized to use this resource' });
 		return this.uploadFile(req, res, db, user);
 	}

+ 1 - 1
src/api/utils/Util.js

@@ -108,7 +108,7 @@ class Util {
 			const filename = randomstring.generate({
 				length: parseInt(process.env.GENERATED_FILENAME_LENGTH, 10),
 				capitalization: 'lowercase'
-			}) + path.extname(name);
+			}) + path.extname(name).toLowerCase();
 			const exists = jetpack.exists(path.join(__dirname, '..', '..', '..', process.env.UPLOAD_FOLDER, filename));
 			if (!exists) return filename;
 			if (i < 5) return retry(i + 1);

+ 1 - 1
src/site/components/grid/Grid.vue

@@ -104,7 +104,7 @@
 					</b-tooltip>
 					<b-tooltip label="Albums"
 						position="is-top">
-						<a @click="manageAlbums(item)">
+						<a @click="$parent.openAlbumModal(item)">
 							<i class="icon-interface-window" />
 						</a>
 					</b-tooltip>

+ 64 - 8
src/site/pages/dashboard/index.vue

@@ -4,6 +4,8 @@
 	section.hero div.hero-body {
 		align-items: baseline;
 	}
+
+	.albumsModal .columns .column { padding: .25rem; }
 </style>
 <style lang="scss">
 	@import '~/assets/styles/_colors.scss';
@@ -16,22 +18,40 @@
 			<div class="container">
 				<div class="columns">
 					<div class="column is-narrow">
-						<Sidebar/>
+						<Sidebar />
 					</div>
 					<div class="column">
 						<h2 class="subtitle">Your uploaded files</h2>
 						<hr>
-						<!--
-						<h1 class="title">Uploads</h1>
-						<h2 class="subtitle">Keep track of all your uploads in here</h2>
-						<hr>
-						-->
 						<Grid v-if="files.length"
 							:files="files" />
 					</div>
 				</div>
 			</div>
 		</div>
+
+		<b-modal :active.sync="isAlbumsModalActive"
+			:width="640"
+			scroll="keep">
+			<div class="card albumsModal">
+				<div class="card-content">
+					<div class="content">
+						<h3 class="subtitle">Select the albums this file should be a part of</h3>
+						<hr>
+						<div class="columns is-multiline">
+							<div v-for="(album, index) in albums"
+								:key="index"
+								class="column is-3">
+								<div class="field">
+									<b-checkbox :value="isAlbumSelected(album.id)"
+										@input="albumCheckboxClicked($event, album.id)">{{ album.name }}</b-checkbox>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</b-modal>
 	</section>
 </template>
 
@@ -45,7 +65,12 @@ export default {
 		Grid
 	},
 	data() {
-		return { files: [] };
+		return {
+			files: [],
+			albums: [],
+			isAlbumsModalActive: false,
+			showingModalForFile: null
+		};
 	},
 	computed: {
 		config() {
@@ -57,6 +82,7 @@ export default {
 	},
 	mounted() {
 		this.getFiles();
+		this.getAlbums();
 		this.$ga.page({
 			page: '/dashboard',
 			title: 'Dashboard',
@@ -64,14 +90,44 @@ export default {
 		});
 	},
 	methods: {
+		isAlbumSelected(id) {
+			if (!this.showingModalForFile) return;
+			const found = this.showingModalForFile.albums.find(el => el.id === id);
+			return found ? found.id ? true : false : false;
+		},
+		openAlbumModal(file) {
+			this.showingModalForFile = file;
+			this.isAlbumsModalActive = true;
+		},
+		async albumCheckboxClicked(value, id) {
+			try {
+				const response = await this.axios.post(`${this.config.baseURL}/file/album/${value ? 'add' : 'del'}`, {
+					albumId: id,
+					fileId: this.showingModalForFile.id
+				});
+				this.$toast.open(response.data.message);
+
+				// Not the prettiest solution to refetch on each click but it'll do for now
+				this.getFiles();
+			} catch (error) {
+				this.$onPromiseError(error);
+			}
+		},
 		async getFiles() {
 			try {
 				const response = await this.axios.get(`${this.config.baseURL}/files`);
 				this.files = response.data.files;
-				console.log(this.files);
 			} catch (error) {
 				console.error(error);
 			}
+		},
+		async getAlbums() {
+			try {
+				const response = await this.axios.get(`${this.config.baseURL}/albums/dropdown`);
+				this.albums = response.data.albums;
+			} catch (error) {
+				this.$onPromiseError(error);
+			}
 		}
 	}
 };