Browse Source

Merge pull request #9 from Musare/staging

Beta Fixes
Jonathan 8 years ago
parent
commit
d435fc5d23

+ 2 - 1
backend/logic/actions/hooks/index.js

@@ -2,5 +2,6 @@
 
 module.exports = {
 	loginRequired: require('./loginRequired'),
-	adminRequired: require('./adminRequired')
+	adminRequired: require('./adminRequired'),
+	ownerRequired: require('./ownerRequired')
 };

+ 30 - 0
backend/logic/actions/hooks/ownerRequired.js

@@ -0,0 +1,30 @@
+const cache = require('../../cache');
+const db = require('../../db');
+const stations = require('../../stations');
+
+module.exports = function(next) {
+	return function(session, stationId) {
+		let args = [];
+		for (let prop in arguments) args.push(arguments[prop]);
+		let cb = args[args.length - 1];
+		cache.hget('sessions', session.sessionId, (err, session) => {
+			if (err || !session || !session.userId) return cb({ status: 'failure', message: 'Login required.' });
+			db.models.user.findOne({_id: session.userId}, (err, user) => {
+				if (err || !user) return cb({ status: 'failure', message: 'Login required.' });
+				if (user.role === 'admin') func();
+				else {
+					stations.getStation(stationId, (err, station) => {
+						if (err || !station) return cb({ status: 'failure', message: 'Something went wrong when getting the station.' });
+						else if (station.type === 'community' && station.owner === session.userId) func();
+						else return cb({ status: 'failure', message: 'Invalid permissions.' });
+					});
+				}
+
+				function func() {
+					args.push(session.userId);
+					next.apply(null, args);
+				}
+			});
+		});
+	}
+};

+ 11 - 44
backend/logic/actions/playlists.js

@@ -7,6 +7,7 @@ const utils = require('../utils');
 const hooks = require('./hooks');
 const async = require('async');
 const playlists = require('../playlists');
+const songs = require('../songs');
 
 let lib = {
 
@@ -27,20 +28,9 @@ let lib = {
 				return (data) ? next() : cb({ 'status': 'failure', 'message': 'Invalid data' });
 			},
 
-			// check the cache for the playlist
-			(next) => cache.hget('playlists', data._id, next),
-
-			// if the cached version exist
-			(playlist, next) => {
-				if (playlist) return next({ 'status': 'failure', 'message': 'A playlist with that id already exists' });
-				db.models.playlist.findOne({ _id: data._id }, next);
-			},
-
-			(playlist, next) => {
-				if (playlist) return next({ 'status': 'failure', 'message': 'A playlist with that id already exists' });
-				const { _id, displayName, songs, createdBy } = data;
+			(next) => {
+				const { name, displayName, songs, createdBy } = data;
 				db.models.playlist.create({
-					_id,
 					displayName,
 					songs,
 					createdBy,
@@ -74,20 +64,14 @@ let lib = {
 	addSongToPlaylist: (session, songId, playlistId, cb) => {
 		async.waterfall([
 			(next) => {
-				utils.getSongFromYouTube(songId, (song) => {
-					song.artists = [];
-					song.genres = [];
-					song.skipDuration = 0;
-					song.thumbnail = 'empty';
-					song.explicit = false;
-					song.requestedBy = 'temp';
-					song.requestedAt = Date.now();
-					next(null, song);
-				});
-			},
-			(newSong, next) => {
-				utils.getSongFromSpotify(newSong, (song) => {
-					next(null, song);
+				songs.getSong(songId, (err, song) => {
+					if (err) {
+						utils.getSongFromYouTube(songId, (song) => {
+							next(null, song);
+						});
+					} else {
+						next(null, {_id: songId, title: song.title, duration: song.duration});
+					}
 				});
 			},
 			(newSong, next) => {
@@ -169,23 +153,6 @@ let lib = {
 		});
 	},
 
-	updatePlaylistId: (session, oldId, newId, cb) => {
-		db.models.playlist.findOne({ _id: oldId }).exec((err, doc) => {
-			if (err) throw err;
-			doc._id = newId;
-			let newPlaylist = new db.models.playlist(doc);
-			newPlaylist.isNew = true;
-			newPlaylist.save(err => {
-				if (err) console.error(err);
-			});
-			db.models.playlist.remove({ _id: oldId });
-			cache.hdel('playlists', oldId, () => {
-				cache.hset('playlists', newId, doc);
-				return cb({ status: 'success', data: doc });
-			});
-		});
-	},
-
 	promoteSong: (session, playlistId, fromIndex, cb) => {
 		db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
 			if (err) throw err;

+ 115 - 76
backend/logic/actions/stations.js

@@ -13,14 +13,6 @@ const stations = require('../stations');
 const songs = require('../songs');
 const hooks = require('./hooks');
 
-cache.sub('station.locked', stationId => {
-	io.io.to(`station.${stationId}`).emit("event:stations.locked");
-});
-
-cache.sub('station.unlocked', stationId => {
-	io.io.to(`station.${stationId}`).emit("event:stations.unlocked");
-});
-
 cache.sub('station.pause', stationId => {
 	io.io.to(`station.${stationId}`).emit("event:stations.pause");
 });
@@ -41,8 +33,34 @@ cache.sub('station.queueUpdate', stationId => {
 
 cache.sub('station.create', stationId => {
 	stations.initializeStation(stationId, (err, station) => {
+		console.log("*************", err, station);
 		//TODO Emit to admin station page
-		io.io.to('home').emit("event:stations.created", station);
+
+		// TODO If community, check if on whitelist
+		console.log("*************", station.privacy);
+		if (station.privacy === 'public') io.io.to('home').emit("event:stations.created", station);
+		else {
+			let sockets = io.io.to('home').sockets;
+			console.log("*************", sockets.length);
+			for (let socketId in sockets) {
+				let socket = sockets[socketId];
+				let session = sockets[socketId].session;
+				console.log("*************", session);
+				if (session.sessionId) {
+					cache.hget('sessions', session.sessionId, (err, session) => {
+						console.log("*************", err, session);
+						if (!err && session) {
+							console.log("*************");
+							db.models.user.findOne({_id: session.userId}, (err, user) => {
+								console.log("*************", err, user.role, station.type, station.owner, session.userId);
+								if (user.role === 'admin') socket.emit("event:stations.created", station);
+								else if (station.type === "community" && station.owner === session.userId) socket.emit("event:stations.created", station);
+							});
+						}
+					});
+				}
+			}
+		}
 	});
 });
 
@@ -56,8 +74,6 @@ module.exports = {
 	 * @return {{ status: String, stations: Array }}
 	 */
 	index: (session, cb) => {
-		// TODO: the logic should be a bit more personalized to the users preferred genres
-		// and it should probably just a different cache table then 'stations'
 		cache.hgetall('stations', (err, stations) => {
 
 			if (err && err !== true) {
@@ -72,6 +88,7 @@ module.exports = {
 			for (let prop in stations) {
 				// TODO If community, check if on whitelist
 				let station = stations[prop];
+				console.log(station)
 				if (station.privacy === 'public') add(true, station);
 				else if (!session.sessionId) add(false);
 				else {
@@ -129,9 +146,7 @@ module.exports = {
 
 		stations.getStation(stationId, (err, station) => {
 
-			if (err && err !== true) {
-				return cb({ status: 'error', message: 'An error occurred while joining the station' });
-			}
+			if (err && err !== true) return cb({ status: 'error', message: 'An error occurred while joining the station' });
 
 			if (station) {
 
@@ -172,7 +187,10 @@ module.exports = {
 									timePaused: station.timePaused,
 									description: station.description,
 									displayName: station.displayName,
-									privacy: station.privacy
+									privacy: station.privacy,
+									partyMode: station.partyMode,
+									owner: station.owner,
+									privatePlaylist: station.privatePlaylist
 								}
 							});
 						});
@@ -187,7 +205,8 @@ module.exports = {
 								timePaused: station.timePaused,
 								description: station.description,
 								displayName: station.displayName,
-								privacy: station.privacy
+								privacy: station.privacy,
+								owner: station.owner
 							}
 						});
 					}
@@ -234,7 +253,7 @@ module.exports = {
 		});
 	},*/
 
-	forceSkip: hooks.adminRequired((session, stationId, cb) => {
+	forceSkip: hooks.ownerRequired((session, stationId, cb) => {
 		stations.getStation(stationId, (err, station) => {
 
 			if (err && err !== true) {
@@ -280,7 +299,7 @@ module.exports = {
 		});
 	},
 
-	updateDisplayName: hooks.adminRequired((session, stationId, newDisplayName, cb) => {
+	updateDisplayName: hooks.ownerRequired((session, stationId, newDisplayName, cb) => {
 		db.models.station.update({_id: stationId}, {$set: {displayName: newDisplayName}}, (err) => {
 			if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the station.' });
 			stations.updateStation(stationId, () => {
@@ -290,7 +309,7 @@ module.exports = {
 		});
 	}),
 
-	updateDescription: hooks.adminRequired((session, stationId, newDescription, cb) => {
+	updateDescription: hooks.ownerRequired((session, stationId, newDescription, cb) => {
 		db.models.station.update({_id: stationId}, {$set: {description: newDescription}}, (err) => {
 			if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the station.' });
 			stations.updateStation(stationId, () => {
@@ -300,7 +319,7 @@ module.exports = {
 		});
 	}),
 
-	updatePrivacy: hooks.adminRequired((session, stationId, newPrivacy, cb) => {
+	updatePrivacy: hooks.ownerRequired((session, stationId, newPrivacy, cb) => {
 		db.models.station.update({_id: stationId}, {$set: {privacy: newPrivacy}}, (err) => {
 			if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the station.' });
 			stations.updateStation(stationId, () => {
@@ -310,7 +329,22 @@ module.exports = {
 		});
 	}),
 
-	pause: hooks.adminRequired((session, stationId, cb) => {
+	updatePartyMode: hooks.ownerRequired((session, stationId, newPartyMode, cb) => {
+		stations.getStation(stationId, (err, station) => {
+			if (err) return cb({ status: 'failure', message: err });
+			if (station.partyMode === newPartyMode) return cb({ status: 'failure', message: 'The party mode was already ' + ((newPartyMode) ? 'enabled.' : 'disabled.') });
+			db.models.station.update({_id: stationId}, {$set: {partyMode: newPartyMode}}, (err) => {
+				if (err) return cb({ status: 'failure', message: 'Something went wrong when saving the station.' });
+				stations.updateStation(stationId, () => {
+					//TODO Pub/sub for privacy change
+					stations.skipStation(stationId)();
+					cb({ status: 'success', message: 'Successfully updated the party mode.' });
+				})
+			});
+		});
+	}),
+
+	pause: hooks.ownerRequired((session, stationId, cb) => {
 		stations.getStation(stationId, (err, station) => {
 			if (err && err !== true) {
 				return cb({ status: 'error', message: 'An error occurred while pausing the station' });
@@ -336,7 +370,7 @@ module.exports = {
 		});
 	}),
 
-	resume: hooks.adminRequired((session, stationId, cb) => {
+	resume: hooks.ownerRequired((session, stationId, cb) => {
 		stations.getStation(stationId, (err, station) => {
 			if (err && err !== true) {
 				return cb({ status: 'error', message: 'An error occurred while resuming the station' });
@@ -360,14 +394,17 @@ module.exports = {
 		});
 	}),
 
-	remove: hooks.adminRequired((session, stationId, cb) => {
-		db.models.station.remove({ _id: stationId });
-		cache.hdel('stations', stationId, () => {
-			return cb({ status: 'success', message: 'Station successfully removed' });
+	remove: hooks.ownerRequired((session, stationId, cb) => {
+		db.models.station.remove({ _id: stationId }, (err) => {
+			console.log(err, stationId);
+			if (err) return cb({status: 'failure', message: 'Something went wrong when deleting that station.'});
+			cache.hdel('stations', stationId, () => {
+				return cb({ status: 'success', message: 'Station successfully removed' });
+			});
 		});
 	}),
 
-	create: hooks.adminRequired((session, data, cb) => {
+	create: hooks.loginRequired((session, data, cb) => {
 		async.waterfall([
 
 			(next) => {
@@ -385,61 +422,41 @@ module.exports = {
 
 			(station, next) => {
 				if (station) return next({ 'status': 'failure', 'message': 'A station with that id already exists' });
-				const { _id, displayName, description, genres, playlist } = data;
-				db.models.station.create({
-					_id,
-					displayName,
-					description,
-					type: "official",
-					playlist,
-					genres,
-					currentSong: stations.defaultSong
-				}, next);
+				const { _id, displayName, description, genres, playlist, type } = data;
+				if (type == 'official') {
+					db.models.station.create({
+						_id,
+						displayName,
+						description,
+						type,
+						privacy: 'private',
+						playlist,
+						genres,
+						currentSong: stations.defaultSong
+					}, next);
+				} else if (type == 'community') {
+					cache.hget('sessions', session.sessionId, (err, session) => {
+						db.models.station.create({
+							_id,
+							displayName,
+							description,
+							type,
+							privacy: 'private',
+							owner: session.userId,
+							queue: [],
+							currentSong: null
+						}, next);
+					});
+				}
 			}
 
 		], (err, station) => {
-			if (err) {console.log(err); return cb({ 'status': 'failure', 'message': 'Something went wrong.'});}
-			cache.pub('station.create', data._id);
+			if (err) console.log(err); return cb({ 'status': 'failure', 'message': 'Something went wrong.'});
 			return cb(null, { 'status': 'success', 'message': 'Successfully created station.' });
-		});
-	}),
-
-	createCommunity: hooks.loginRequired((session, data, cb) => {
-		async.waterfall([
-
-			(next) => {
-				return (data) ? next() : cb({ 'status': 'failure', 'message': 'Invalid data' });
-			},
-
-			// check the cache for the station
-			(next) => cache.hget('stations', data._id, next),
-
-			// if the cached version exist
-			(station, next) => {
-				if (station) return next({ 'status': 'failure', 'message': 'A station with that id already exists' });
-				db.models.station.findOne({ _id: data._id }, next);
-			},
-
-			(station, next) => {
-				if (station) return next({ 'status': 'failure', 'message': 'A station with that id already exists' });
-				const { _id, displayName, description } = data;
-				db.models.station.create({
-					_id,
-					displayName,
-					description,
-					type: "community",
-					queue: [],
-					currentSong: null
-				}, next);
-			}
-
-		], (err, station) => {
-			if (err) console.error(err);
 			cache.pub('station.create', data._id);
-			return cb({ 'status': 'success', 'message': 'Successfully created station.' });
 		});
 	}),
-
+	
 	addToQueue: hooks.loginRequired((session, stationId, songId, cb, userId) => {
 		stations.getStation(stationId, (err, station) => {
 			if (err) return cb(err);
@@ -481,7 +498,7 @@ module.exports = {
 		});
 	}),
 
-	removeFromQueue: hooks.adminRequired((session, stationId, songId, cb, userId) => {
+	removeFromQueue: hooks.ownerRequired((session, stationId, songId, cb, userId) => {
 		stations.getStation(stationId, (err, station) => {
 			if (err) return cb(err);
 			if (station.type === 'community') {
@@ -513,4 +530,26 @@ module.exports = {
 		});
 	}),
 
+	selectPrivatePlaylist: hooks.csOwnerRequired((session, stationId, playlistId, cb, userId) => {
+		stations.getStation(stationId, (err, station) => {
+			if (err) return cb(err);
+			if (station.type === 'community') {
+				if (station.privatePlaylist === playlistId) return cb({'status': 'failure', 'message': 'That playlist is already selected.'});
+				db.models.playlist.findOne({_id: playlistId}, (err, playlist) => {
+					if (err) return cb(err);
+					if (playlist) {
+						db.models.station.update({_id: stationId}, {$set: {privatePlaylist: playlistId, currentSongIndex: 0}}, (err) => {
+							if (err) return cb(err);
+							stations.updateStation(stationId, (err, station) => {
+								if (err) return cb(err);
+								stations.skipStation(stationId)();
+								cb({'status': 'success', 'message': 'Playlist selected.'});
+							});
+						});
+					} else cb({'status': 'failure', 'message': 'Playlist not found.'});
+				});
+			} else cb({'status': 'failure', 'message': 'That station is not a community station.'});
+		});
+	}),
+
 };

+ 5 - 2
backend/logic/actions/users.js

@@ -14,11 +14,12 @@ module.exports = {
 
 	login: (session, identifier, password, cb) => {
 
+		identifier = identifier.toLowerCase();
+
 		async.waterfall([
 
 			// check if a user with the requested identifier exists
 			(next) => db.models.user.findOne({
-				//TODO Handle lowercase
 				$or: [{ 'username': identifier }, { 'email.address': identifier }]
 			}, next),
 
@@ -178,7 +179,9 @@ module.exports = {
 						email: account.email.address,
 						password: '',
 						createdAt: account.createdAt,
-						statistics: account.statistics
+						statistics: account.statistics,
+						liked: account.liked,
+						disliked: account.disliked
 					}
 				});
 			}

+ 1 - 1
backend/logic/db/schemas/playlist.js

@@ -1,5 +1,5 @@
 module.exports = {
-	_id: { type: String, lowercase: true, max: 16, min: 2, index: true, unique: true, required: true },
+	name: { type: String, lowercase: true, max: 16, min: 2 },
 	displayName: { type: String, min: 2, max: 32, required: true },
 	songs: { type: Array },
 	createdBy: { type: String, required: true },

+ 4 - 2
backend/logic/db/schemas/station.js

@@ -1,7 +1,7 @@
 module.exports = {
 	_id: { type: String, lowercase: true, max: 16, min: 2, index: true, unique: true, required: true },
 	type: { type: String, enum: ["official", "community"], required: true },
-	displayName: { type: String, min: 2, max: 32, required: true },
+	displayName: { type: String, min: 2, max: 32, required: true, unique: true },
 	description: { type: String, min: 2, max: 128, required: true },
 	paused: { type: Boolean, default: false, required: true },
 	currentSong: {
@@ -33,5 +33,7 @@ module.exports = {
 		dislikes: { type: Number, default: -1 },
 		requestedBy: { type: String, required: true }
 	}],
-	owner: { type: String }
+	owner: { type: String },
+	privatePlaylist: { type: String },
+	partyMode: { type: Boolean }
 };

+ 1 - 3
backend/logic/db/schemas/user.js

@@ -21,9 +21,7 @@ module.exports = {
 		bannedUntil: Date
 	},
 	statistics: {
-		songsRequested: { type: Number, default: 0, required: true },
-		songsDisliked: [{ type: String, default: '', required: true }],
-		songsLiked: [{ type: String, default: '', required: true }]
+		songsRequested: { type: Number, default: 0, required: true }
 	},
 	liked: [{ type: String }],
 	disliked: [{ type: String }],

+ 3 - 1
backend/logic/io.js

@@ -92,11 +92,13 @@ module.exports = {
 						db.models.user.findOne({ _id: session.userId }, (err, user) => {
 							let role = '';
 							let username = '';
+							let userId = '';
 							if (user) {
 								role = user.role;
 								username = user.username;
+								userId = session.userId;
 							}
-							socket.emit('ready', true, role, username);
+							socket.emit('ready', true, role, username, userId);
 						});
 					} else socket.emit('ready', false);
 				})

+ 78 - 23
backend/logic/stations.js

@@ -43,7 +43,7 @@ module.exports = {
 	},
 
 	initializeStation: function(stationId, cb) {
-		console.log(112233, stationId);
+		console.log(112233, stationId, cb);
 		if (typeof cb !== 'function') cb = ()=>{};
 		let _this = this;
 		_this.getStation(stationId, (err, station) => {
@@ -63,18 +63,26 @@ module.exports = {
 							if (isNaN(timeLeft)) timeLeft = -1;
 							if (station.currentSong.duration * 1000 < timeLeft || timeLeft < 0) {
 								console.log("Test");
-								_this.skipStation(station._id)();
+								this.skipStation(station._id)((err, station) => {
+									console.log(45, err, station);
+									cb(err, station);
+								});
 							} else {
 								notifications.schedule(`stations.nextSong?id=${station._id}`, timeLeft);
+								cb(null, station);
 							}
 						} else {
-							_this.skipStation(station._id)();
+							_this.skipStation(station._id)((err, station) => {
+								console.log(47, err, station);
+								cb(err, station);
+							});
 						}
 					} else {
 						notifications.unschedule(`stations.nextSong?id${station._id}`);
+						cb(null, station);
 					}
-				}
-			}
+				} else cb("Station not found.");
+			} else cb(err);
 		});
 	},
 
@@ -175,7 +183,8 @@ module.exports = {
 
 	skipStation: function(stationId) {
 		let _this = this;
-		return () => {
+		return (cb) => {
+			if (typeof cb !== 'function') cb = ()=>{};
 			console.log("###2");
 			console.log("NOTIFICATION!!!");
 			_this.getStation(stationId, (err, station) => {
@@ -294,23 +303,67 @@ module.exports = {
 									});
 								}
 							} else {
-								if (station.queue.length > 0) {
-									console.log("##");
-									db.models.station.update({_id: stationId}, {$pull: {queue: {songId: station.queue[0]._id}}}, (err) => {
-										console.log("##1", err);
-										if (err) return next(err);
-										let $set = {};
-										$set.currentSong = station.queue[0];
-										$set.startedAt = Date.now();
-										$set.timePaused = 0;
-										if (station.paused) {
-											$set.pausedAt = Date.now();
+								if (station.partyMode === true) {
+									if (station.queue.length > 0) {
+										console.log("##");
+										db.models.station.update({_id: stationId}, {$pull: {queue: {songId: station.queue[0]._id}}}, (err) => {
+											console.log("##1", err);
+											if (err) return next(err);
+											let $set = {};
+											$set.currentSong = station.queue[0];
+											$set.startedAt = Date.now();
+											$set.timePaused = 0;
+											if (station.paused) {
+												$set.pausedAt = Date.now();
+											}
+											next(null, $set);
+										});
+									} else {
+										console.log("##2");
+										next(null, {currentSong: null});
+									}
+								} else {
+									db.models.playlist.findOne({_id: station.privatePlaylist}, (err, playlist) => {
+										console.log(station.privatePlaylist, err, playlist);
+										if (err || !playlist) return next(null, {currentSong: null});
+										playlist = playlist.songs;
+										if (playlist.length > 0) {
+											let $set = {};
+											if (station.currentSongIndex < playlist.length - 1) {
+												$set.currentSongIndex = station.currentSongIndex + 1;
+											} else {
+												$set.currentSongIndex = 0;
+											}
+											songs.getSong(playlist[$set.currentSongIndex]._id, (err, song) => {
+												if (!err && song) {
+													$set.currentSong = {
+														_id: song._id,
+														title: song.title,
+														artists: song.artists,
+														duration: song.duration,
+														likes: song.likes,
+														dislikes: song.dislikes,
+														skipDuration: song.skipDuration,
+														thumbnail: song.thumbnail
+													};
+												} else {
+													let song = playlist[$set.currentSongIndex];
+													$set.currentSong = {
+														_id: song._id,
+														title: song.title,
+														duration: song.duration,
+														likes: -1,
+														dislikes: -1
+													};
+												}
+												$set.startedAt = Date.now();
+												$set.timePaused = 0;
+												next(null, $set);
+											});
+										} else {
+											next(null, {currentSong: null});
 										}
-										next(null, $set);
 									});
-								} else {
-									console.log("##2");
-									next(null, {currentSong: null});
 								}
 							}
 						},
@@ -321,7 +374,7 @@ module.exports = {
 								console.log("##2.5", err);
 								_this.updateStation(station._id, (err, station) => {
 									console.log("##2.6", err);
-									if (station.type === 'community') {
+									if (station.type === 'community' && station.partyMode === true) {
 										cache.pub('station.queueUpdate', stationId);
 									}
 									next(null, station);
@@ -349,13 +402,15 @@ module.exports = {
 								console.log("22", !!(station.currentSong));
 								utils.socketsLeaveSongRooms(io.io.to(`station.${station._id}`).sockets, `song.${station.currentSong._id}`);
 							}
-						}
+							cb(null, station);
+						} else cb(err);
 					});
 				}
 				// the station doesn't exist anymore, unsubscribe from it
 				else {
 					console.log(112233445566, "REMOVE NOTIFICATION");
 					notifications.remove(notification);
+					cb("Station not found.");
 				}
 			});
 		}

+ 8 - 21
frontend/App.vue

@@ -5,7 +5,7 @@
 		<what-is-new></what-is-new>
 		<login-modal v-if='isLoginActive'></login-modal>
 		<register-modal v-if='isRegisterActive'></register-modal>
-		<create-community-station v-if='isCCSActive'></create-community-station>
+		<create-community-station v-if='isCreateCommunityStationActive'></create-community-station>
 	</div>
 </template>
 
@@ -31,17 +31,13 @@
 					email: '',
 					password: ''
 				},
-				ccs: {
-					name: '',
-					displayName: '',
-					description: ''
-				},
 				loggedIn: false,
 				role: '',
 				username: '',
+				userId: '',
 				isRegisterActive: false,
 				isLoginActive: false,
-				isCCSActive: false,
+				isCreateCommunityStationActive: false,
 				serverDomain: ''
 			}
 		},
@@ -60,11 +56,12 @@
 		},
 		ready() {
 			let _this = this;
-			auth.getStatus((authenticated, role, username) => {
+			auth.getStatus((authenticated, role, username, userId) => {
 				_this.socket = window.socket;
 				_this.loggedIn = authenticated;
 				_this.role = role;
 				_this.username = username;
+				_this.userId = userId;
 			});
 			lofig.get('serverDomain', res => {
 				_this.serverDomain = res;
@@ -104,16 +101,6 @@
 					}
 				});
 			},
-			'ccs': function () {
-				let _this = this;
-				this.socket.emit('stations.createCommunity', {_id: _this.ccs.name, displayName: _this.ccs.displayName, description: _this.ccs.description}, result => {
-					if (result.status === 'success') {
-						Toast.methods.addToast(`You have added the station successfully`, 4000);
-					} else {
-						Toast.methods.addToast(result.message, 4000);
-					}
-				});
-			},
 			'toggleModal': function (type) {
 				switch(type) {
 					case 'register':
@@ -122,12 +109,12 @@
 					case 'login':
 						this.isLoginActive = !this.isLoginActive;
 						break;
-					case 'ccs':
-						this.isCCSActive = !this.isCCSActive;
+					case 'createCommunityStation':
+						this.isCreateCommunityStationActive = !this.isCreateCommunityStationActive;
 						break;
 				}
 			}
 		},
 		components: { Toast, WhatIsNew, LoginModal, RegisterModal, CreateCommunityStation }
 	}
-</script>
+</script>

+ 5 - 3
frontend/auth.js

@@ -5,20 +5,22 @@ export default {
 	ready: false,
 	authenticated: false,
 	username: '',
+	userId: '',
 	role: 'default',
 
 	getStatus: function (cb) {
-		if (this.ready) cb(this.authenticated, this.role, this.username);
+		if (this.ready) cb(this.authenticated, this.role, this.username, this.userId);
 		else callbacks.push(cb);
 	},
 
-	data: function (authenticated, role, username) {
+	data: function (authenticated, role, username, userId) {
 		this.authenticated = authenticated;
 		this.role = role;
 		this.username = username;
+		this.userId = userId;
 		this.ready = true;
 		callbacks.forEach(callback => {
-			callback(authenticated, role, username);
+			callback(authenticated, role, username, userId);
 		});
 		callbacks = [];
 	}

+ 8 - 4
frontend/components/Admin/QueueSongs.vue

@@ -95,14 +95,18 @@
 					for (let z = 0; z < this.editing.song.genres.length; z++) {
 						if (this.editing.song.genres[z] == $('#new-genre').val()) return Toast.methods.addToast('Genre already exists', 3000);
 					}
-					if ($('#new-genre').val() !== '') this.editing.song.genres.push($('#new-genre').val());
-					else Toast.methods.addToast('Genre cannot be empty', 3000);
+					if ($('#new-genre').val() !== '') {
+						this.editing.song.genres.push($('#new-genre').val());
+						$('#new-genre').val('');
+					} else Toast.methods.addToast('Genre cannot be empty', 3000);
 				} else if (type == 'artists') {
 					for (let z = 0; z < this.editing.song.artists.length; z++) {
 						if (this.editing.song.artists[z] == $('#new-artist').val()) return Toast.methods.addToast('Artist already exists', 3000);
 					}
-					if ($('#new-artist').val() !== '') this.editing.song.artists.push($('#new-artist').val());
-					else Toast.methods.addToast('Artist cannot be empty', 3000);
+					if ($('#new-artist').val() !== '') {
+						this.editing.song.artists.push($('#new-artist').val());
+						$('#new-artist').val('');
+					} else Toast.methods.addToast('Artist cannot be empty', 3000);
 				}
 			},
 			removeTag: function (type, index) {

+ 8 - 4
frontend/components/Admin/Songs.vue

@@ -94,14 +94,18 @@
 					for (let z = 0; z < this.editing.song.genres.length; z++) {
 						if (this.editing.song.genres[z] == $('#new-genre').val()) return Toast.methods.addToast('Genre already exists', 3000);
 					}
-					if ($('#new-genre').val() !== '') this.editing.song.genres.push($('#new-genre').val());
-					else Toast.methods.addToast('Genre cannot be empty', 3000);
+					if ($('#new-genre').val() !== '') {
+						this.editing.song.genres.push($('#new-genre').val());
+						$('#new-genre').val('');
+					} else Toast.methods.addToast('Genre cannot be empty', 3000);
 				} else if (type == 'artists') {
 					for (let z = 0; z < this.editing.song.artists.length; z++) {
 						if (this.editing.song.artists[z] == $('#new-artist').val()) return Toast.methods.addToast('Artist already exists', 3000);
 					}
-					if ($('#new-artist').val() !== '') this.editing.song.artists.push($('#new-artist').val());
-					else Toast.methods.addToast('Artist cannot be empty', 3000);
+					if ($('#new-artist').val() !== '') {
+						this.editing.song.artists.push($('#new-artist').val());
+						$('#new-artist').val('');
+					} else Toast.methods.addToast('Artist cannot be empty', 3000);
 				}
 			},
 			removeTag: function (type, index) {

+ 1 - 1
frontend/components/Admin/Stations.vue

@@ -30,7 +30,7 @@
 						</td>
 						<td>
 							<div class='control'>
-								<input v-for='song in station.playlist' track-by='$index' class='input' type='text' :value='song.id' v-model='song.id'>
+								<span v-for='song in station.playlist' track-by='$index'>{{ song }}</span>
 							</p>
 						</td>
 						<td>

+ 39 - 7
frontend/components/Modals/CreateCommunityStation.vue

@@ -8,17 +8,17 @@
 			</header>
 			<section class='modal-card-body'>
 				<!-- validation to check if exists http://bulma.io/documentation/elements/form/ -->
-				<label class='label'>Name (lowercase, a-z, used in the url)</label>
+				<label class='label'>Unique ID (lowercase, a-z, used in the url)</label>
 				<p class='control'>
-					<input class='input' type='text' placeholder='Name...' v-model='$parent.ccs.name'>
+					<input class='input' type='text' placeholder='Name...' v-model='newCommunity._id'>
 				</p>
-				<label class='label'>Display name</label>
+				<label class='label'>Display Name</label>
 				<p class='control'>
-					<input class='input' type='text' placeholder='Display name...' v-model='$parent.ccs.displayName'>
+					<input class='input' type='text' placeholder='Display name...' v-model='newCommunity.displayName'>
 				</p>
 				<label class='label'>Description</label>
 				<p class='control'>
-					<input class='input' type='text' placeholder='Description...' v-model='$parent.ccs.description'>
+					<input class='input' type='text' placeholder='Description...' v-model='newCommunity.description'>
 				</p>
 			</section>
 			<footer class='modal-card-foot'>
@@ -29,13 +29,45 @@
 </template>
 
 <script>
+	import { Toast } from 'vue-roaster';
+
 	export default {
+		data() {
+			return {
+				newCommunity: {
+					_id: '',
+					displayName: '',
+					description: ''
+				}
+			}
+		},
+		ready: function () {
+			let _this = this;
+			let socketInterval = setInterval(() => {
+				if (!!_this.$parent.socket) {
+					_this.socket = _this.$parent.socket;
+					clearInterval(socketInterval);
+				}
+			}, 100);
+		},
 		methods: {
 			toggleModal: function () {
-				this.$dispatch('toggleModal', 'ccs');
+				this.$dispatch('toggleModal', 'createCommunityStation');
 			},
 			submitModal: function () {
-				this.$dispatch('ccs');
+				let _this = this;
+				if (_this.newCommunity._id == '') return Toast.methods.addToast('ID cannot be a blank field', 3000);
+				if (_this.newCommunity.displayName == '') return Toast.methods.addToast('Display Name cannot be a blank field', 3000);
+				if (_this.newCommunity.description == '') return Toast.methods.addToast('Description cannot be a blank field', 3000);
+				this.socket.emit('stations.create', {
+					_id: _this.newCommunity._id,
+					type: 'community',
+					displayName: _this.newCommunity.displayName,
+					description: _this.newCommunity.description
+				}, res => {
+					if (res.status === 'success') Toast.methods.addToast(`You have added the station successfully`, 4000);
+					else Toast.methods.addToast(res.message, 4000);
+				});
 				this.toggleModal();
 			}
 		}

+ 2 - 2
frontend/components/Modals/EditSong.vue

@@ -60,7 +60,7 @@
 							</p>
 							<span class='tag is-info' v-for='(index, artist) in $parent.editing.song.artists' track-by='$index'>
 								{{ artist }}
-								<button class='delete is-info' @click='$parent.removeTag("artists", index)'></button>
+								<button class='delete is-info' @click='$parent.$parent.removeTag("artists", index)'></button>
 							</span>
 						</div>
 						<div>
@@ -70,7 +70,7 @@
 							</p>
 							<span class='tag is-info' v-for='(index, genre) in $parent.editing.song.genres' track-by='$index'>
 								{{ genre }}
-								<button class='delete is-info' @click='$parent.removeTag("genres", index)'></button>
+								<button class='delete is-info' @click='$parent.$parent.removeTag("genres", index)'></button>
 							</span>
 						</div>
 					</div>

+ 20 - 3
frontend/components/Modals/EditStation.vue

@@ -30,9 +30,9 @@
 					<p class='control is-expanded'>
 						<span class='select'>
 							<select v-model='$parent.station.privacy'>
-								<option v-bind:value=''public''>Public</option>
-								<option v-bind:value=''unlisted''>Unlisted</option>
-								<option v-bind:value=''private''>Private</option>
+								<option :value='"public"'>Public</option>
+								<option :value='"unlisted"'>Unlisted</option>
+								<option :value='"private"'>Private</option>
 							</select>
 						</span>
 					</p>
@@ -40,6 +40,17 @@
 						<a class='button is-info' @click='updatePrivacy()'>Update</a>
 					</p>
 				</div>
+				<div class='control is-grouped' v-if="$parent.type === 'community'">
+					<p class="control is-expanded">
+						<label class="checkbox">
+							<input type="checkbox" v-model="$parent.station.partyMode">
+							Party mode
+						</label>
+					</p>
+					<p class='control'>
+						<a class='button is-info' @click='updatePartyMode()'>Update</a>
+					</p>
+				</div>
 			</section>
 		</div>
 	</div>
@@ -67,6 +78,12 @@
 					if (res.status == 'success') return Toast.methods.addToast(res.message, 4000);
 					Toast.methods.addToast(res.message, 8000);
 				});
+			},
+			updatePartyMode: function () {
+				this.socket.emit('stations.updatePartyMode', this.$parent.stationId, this.$parent.station.partyMode, res => {
+					if (res.status == 'success') return Toast.methods.addToast(res.message, 4000);
+					Toast.methods.addToast(res.message, 8000);
+				});
 			}
 		},
 		ready: function () {

+ 0 - 4
frontend/components/Modals/Playlists/Create.vue

@@ -7,9 +7,6 @@
 				<button class='delete' @click='$parent.toggleModal("createPlaylist")'></button>
 			</header>
 			<section class='modal-card-body'>
-				<p class='control is-expanded'>
-					<input class='input' type='text' placeholder='Playlist ID' v-model='playlist._id'>
-				</p>
 				<p class='control is-expanded'>
 					<input class='input' type='text' placeholder='Playlist Display Name' v-model='playlist.displayName'>
 				</p>
@@ -28,7 +25,6 @@
 		data() {
 			return {
 				playlist: {
-					_id: null,
 					displayName: null,
 					songs: [],
 					createdBy: this.$parent.$parent.username,

+ 2 - 16
frontend/components/Modals/Playlists/Edit.vue

@@ -14,11 +14,11 @@
 							<div class='controls'>
 								<a href='#'>
 									<i class='material-icons' v-if='playlist.songs[0] !== song' @click='promoteSong($index)'>keyboard_arrow_up</i>
-									<i class='material-icons' v-else>error</i>
+									<i class='material-icons' style='opacity: 0' v-else>error</i>
 								</a>
 								<a href='#' @click=''>
 									<i class='material-icons' v-if='playlist.songs.length - 1 !== $index' @click='demoteSong($index)'>keyboard_arrow_down</i>
-									<i class='material-icons' v-else>error</i>
+									<i class='material-icons' style='opacity: 0' v-else>error</i>
 								</a>
 								<a href='#' @click='removeSongFromPlaylist(song._id)'><i class='material-icons'>delete</i></a>
 							</div>
@@ -66,14 +66,6 @@
 						<a class='button is-info' @click='renamePlaylist()'>Rename</a>
 					</p>
 				</div>
-				<div class='control is-grouped'>
-					<p class='control is-expanded'>
-						<input class='input' type='text' placeholder='Playlist ID' v-model='playlist._id'>
-					</p>
-					<p class='control'>
-						<a class='button is-info' @click='renamePlaylistId()'>Rename</a>
-					</p>
-				</div>
 			</section>
 			<footer class='modal-card-foot'>
 				<a class='button is-danger' @click='removePlaylist()'>Remove Playlist</a>
@@ -138,12 +130,6 @@
 					if (res.status == 'success') Toast.methods.addToast(res.message, 3000);
 				});
 			},
-			renamePlaylistId: function () {
-				let _this = this;
-				_this.socket.emit('playlists.updatePlaylistId', _this.playlist.oldId, _this.playlist._id, res => {
-					if (res.status == 'success') _this.playlist = res.data;
-				});
-			},
 			removePlaylist: function () {
 				let _this = this;
 				_this.socket.emit('playlists.remove', _this.playlist._id, res => {

+ 10 - 2
frontend/components/Sidebars/Playlist.vue

@@ -9,7 +9,7 @@
 						<a href='#'>{{ playlist.displayName }}</a>
 						<!--Will play playlist in community station Kris-->
 						<div class='icons-group'>
-							<a href='#' @click=''>
+							<a href='#' @click='selectPlaylist(playlist._id)' v-if="$parent.station && !$parent.station.privatePlaylist === playlist._id">
 								<i class='material-icons'>play_arrow</i>
 							</a>
 							<a href='#' @click='editPlaylist(playlist._id)'>
@@ -28,6 +28,8 @@
 </template>
 
 <script>
+	import { Toast } from 'vue-roaster';
+
 	export default {
 		data() {
 			return {
@@ -35,8 +37,14 @@
 			}
 		},
 		methods: {
-			editPlaylist: function (id) {
+			editPlaylist: function(id) {
 				this.$parent.editPlaylist(id);
+			},
+			selectPlaylist: function(id) {
+				this.socket.emit('stations.selectPrivatePlaylist', this.$parent.stationId, id, (res) => {
+					if (res.status === 'failure') return Toast.methods.addToast(res.message, 8000);
+					Toast.methods.addToast(res.message, 4000);
+				});
 			}
 		},
 		ready: function () {

+ 38 - 45
frontend/components/Station/CommunityHeader.vue

@@ -1,75 +1,65 @@
 <template>
-	<nav class="nav">
-		<div class="nav-left">
-			<a class="nav-item logo" href="#" v-link="{ path: '/' }" @click="this.$dispatch('leaveStation', title)">
+	<nav class='nav'>
+		<div class='nav-left'>
+			<a class='nav-item logo' href='#' v-link='{ path: "/" }' @click='this.$dispatch('leaveStation', title)'>
 				Musare
 			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin'" @click="$parent.toggleModal('editStation')">
-				<span class="icon">
-					<i class="material-icons">settings</i>
+			<a class='nav-item' href='#' v-if='isOwner()' @click='$parent.toggleModal('editStation')'>
+				<span class='icon'>
+					<i class='material-icons'>settings</i>
 				</span>
 			</a>
-			<a v-if="$parent.$parent.role === 'admin'" class="nav-item" href="#" @click="$parent.skipStation()">
-				<span class="icon">
-					<i class="material-icons">skip_next</i>
+			<a v-if='isOwner()' class='nav-item' href='#' @click='$parent.skipStation()'>
+				<span class='icon'>
+					<i class='material-icons'>skip_next</i>
 				</span>
 			</a>
-			<a v-if="$parent.$parent.role !== 'admin' && $parent.$parent.loggedIn" class="nav-item" href="#" @click="$parent.voteSkipStation()">
-				<span class="icon">
-					<i class="material-icons">skip_next</i>
+			<a v-if='!isOwner() && $parent.$parent.loggedIn' class='nav-item' href='#' @click='$parent.voteSkipStation()'>
+				<span class='icon'>
+					<i class='material-icons'>skip_next</i>
 				</span>
 			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && $parent.locked" @click="$parent.unlockStation()">
-				<span class="icon">
-					<i class="material-icons">lock_outline</i>
+			<a class='nav-item' href='#' v-if='isOwner() && $parent.paused' @click='$parent.resumeStation()'>
+				<span class='icon'>
+					<i class='material-icons'>play_arrow</i>
 				</span>
 			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && !$parent.locked" @click="$parent.lockStation()">
-				<span class="icon">
-					<i class="material-icons">lock_open</i>
-				</span>
-			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && $parent.paused" @click="$parent.resumeStation()">
-				<span class="icon">
-					<i class="material-icons">play_arrow</i>
-				</span>
-			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && !$parent.paused" @click="$parent.pauseStation()">
-				<span class="icon">
-					<i class="material-icons">pause</i>
+			<a class='nav-item' href='#' v-if='isOwner() && !$parent.paused' @click='$parent.pauseStation()'>
+				<span class='icon'>
+					<i class='material-icons'>pause</i>
 				</span>
 			</a>
 		</div>
 
-		<div class="nav-center">
+		<div class='nav-center'>
 			{{title}}
 		</div>
 
-		<!--<span class="nav-toggle" :class="{ 'is-active': isActive }" @click="toggleMobileMenu()">
+		<!--<span class='nav-toggle' :class='{ 'is-active': isActive }' @click='toggleMobileMenu()'>
 			<span></span>
 			<span></span>
 			<span></span>
 		</span>-->
 
-		<div class="nav-right">
-			<a class="nav-item" href="#" @click='$parent.sidebars.queue = !$parent.sidebars.queue'>
-				<span class="icon">
-					<i class="material-icons">queue_music</i>
+		<div class='nav-right'>
+			<a class='nav-item' href='#' @click='$parent.sidebars.queue = !$parent.sidebars.queue' v-if='$parent.station.partyMode === true'>
+				<span class='icon'>
+					<i class='material-icons'>queue_music</i>
 				</span>
 			</a>
-			<!--<a class="nav-item" href="#">
-				<span class="icon">
-					<i class="material-icons">chat</i>
+			<!--<a class='nav-item' href='#'>
+				<span class='icon'>
+					<i class='material-icons'>chat</i>
 				</span>
 			</a>-->
-			<!--<a class="nav-item" href="#" @click='$parent.sidebars.users = !$parent.sidebars.users'>
-				<span class="icon">
-					<i class="material-icons">people</i>
+			<!--<a class='nav-item' href='#' @click='$parent.sidebars.users = !$parent.sidebars.users'>
+				<span class='icon'>
+					<i class='material-icons'>people</i>
 				</span>
 			</a>-->
-			<a class="nav-item" href="#" @click='$parent.sidebars.playlist = !$parent.sidebars.playlist'>
-				<span class="icon">
-					<i class="material-icons">library_music</i>
+			<a class='nav-item' href='#' @click='$parent.sidebars.playlist = !$parent.sidebars.playlist'>
+				<span class='icon'>
+					<i class='material-icons'>library_music</i>
 				</span>
 			</a>
 		</div>
@@ -85,14 +75,17 @@
 			}
 		},
 		methods: {
-			toggleMobileMenu: function() {
+			toggleMobileMenu: function () {
 				this.isActive = !this.isActive;
+			},
+			isOwner: function () {
+				return this.$parent.$parent.role === 'admin' || this.$parent.$parent.userId === this.$parent.station.owner
 			}
 		}
 	}
 </script>
 
-<style lang="scss" scoped>
+<style lang='scss' scoped>
 	@import 'theme.scss';
 	.nav {
 		background-color: #03a9f4;

+ 2 - 12
frontend/components/Station/OfficialHeader.vue

@@ -24,16 +24,6 @@
 					<i class="material-icons">skip_next</i>
 				</span>
 			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && $parent.locked" @click="$parent.unlockStation()">
-				<span class="icon">
-					<i class="material-icons">lock_outline</i>
-				</span>
-			</a>
-			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && !$parent.locked" @click="$parent.lockStation()">
-				<span class="icon">
-					<i class="material-icons">lock_open</i>
-				</span>
-			</a>
 			<a class="nav-item" href="#" v-if="$parent.$parent.role === 'admin' && $parent.paused" @click="$parent.resumeStation()">
 				<span class="icon">
 					<i class="material-icons">play_arrow</i>
@@ -57,11 +47,11 @@
 		</span>-->
 
 		<div class="nav-right">
-			<a class="nav-item" href="#" @click='$parent.sidebars.queue = !$parent.sidebars.queue'>
+			<!--a class="nav-item" href="#" @click='$parent.sidebars.queue = !$parent.sidebars.queue'>
 				<span class="icon">
 					<i class="material-icons">queue_music</i>
 				</span>
-			</a>
+			</a-->
 			<!--<a class="nav-item" href="#">
 				<span class="icon">
 					<i class="material-icons">chat</i>

+ 5 - 2
frontend/components/Station/Station.vue

@@ -51,7 +51,7 @@
 						</div>
 					</div>
 					<div class="column is-4-desktop is-12-mobile" v-if="!simpleSong">
-						<img class="image" id="song-thumbnail" style="margin-top: 10px !important" :src="currentSong.thumbnail" alt="Song Thumbnail" />
+						<img class="image" id="song-thumbnail" style="margin-top: 10px !important" :src="currentSong.thumbnail" alt="Song Thumbnail" onerror="this.src='/assets/notes.png'" />
 					</div>
 				</div>
 			</div>
@@ -301,7 +301,10 @@
 							_this.station = {
 								displayName: res.data.displayName,
 								description: res.data.description,
-								privacy: res.data.privacy
+								privacy: res.data.privacy,
+								partyMode: res.data.partyMode,
+								owner: res.data.owner,
+								privatePlaylist: res.data.privatePlaylist
 							};
 							_this.currentSong = (res.data.currentSong) ? res.data.currentSong : {};
 							_this.type = res.data.type;

+ 2 - 2
frontend/components/User/Show.vue

@@ -19,11 +19,11 @@
 				</div>
 				<div class="level-item has-text-centered">
 					<p class="heading">Likes</p>
-					<p class="title">{{ user.statistics.songsLiked.length }}</p>
+					<p class="title">{{ user.liked.length }}</p>
 				</div>
 				<div class="level-item has-text-centered">
 					<p class="heading">Dislikes</p>
-					<p class="title">{{ user.statistics.songsDisliked.length }}</p>
+					<p class="title">{{ user.disliked.length }}</p>
 				</div>
 			</nav>
 		</div>

+ 55 - 24
frontend/components/pages/Home.vue

@@ -1,10 +1,10 @@
 <template>
 	<div class="app">
 		<main-header></main-header>
-		<div class="group" v-if="stations.official.length">
+		<div class="group">
 			<div class="group-title">Official Stations</div>
 			<div class="group-stations">
-				<div class="stations-station" v-for="station in stations.official" v-link="{ path: '/official/' + station._id }" @click="this.$dispatch('joinStation', station._id)">
+				<div class="stations-station" v-for="station in stations.official" v-link="{ path: '/official/' + station._id }" @click="this.$dispatch('joinStation', station._id)" v-bind:class="station.class">
 					<img class="station-image" :src="station.currentSong.thumbnail" onerror="this.src='/assets/notes.png'" />
 					<div class="station-info">
 						<div class="station-grid-left">
@@ -18,11 +18,10 @@
 				</div>
 			</div>
 		</div>
-		<button @click="toggleModal('ccs')">CREATE COMMUNITY STATION</button>
-		<div class="group" v-if="stations.community.length">
-			<div class="group-title">Community Stations</div>
+		<div class="group">
+			<div class="group-title">Community Stations <i class="material-icons ccs-button" @click="toggleModal('createCommunityStation')" v-if="$parent.loggedIn">add</i></div>
 			<div class="group-stations">
-				<div class="stations-station" v-for="station in stations.community" v-link="{ path: '/community/' + station._id }" @click="this.$dispatch('joinStation', station._id)">
+				<div class="stations-station" v-for="station in stations.community" v-link="{ path: '/community/' + station._id }" @click="this.$dispatch('joinStation', station._id)" v-bind:class="station.class">
 					<img class="station-image" :src="station.currentSong.thumbnail" onerror="this.src='/assets/notes.png'" />
 					<div class="station-info">
 						<div class="station-grid-left">
@@ -43,6 +42,7 @@
 <script>
 	import MainHeader from '../MainHeader.vue';
 	import MainFooter from '../MainFooter.vue';
+	import auth from '../../auth';
 
 	export default {
 		data() {
@@ -60,24 +60,38 @@
 		},
 		ready() {
 			let _this = this;
-			let socketInterval = setInterval(() => {
-				if (!!_this.$parent.socket) {
-					_this.socket = _this.$parent.socket;
-					_this.socket.emit("stations.index", data => {
-						if (data.status === "success")  data.stations.forEach(station => {
-							if (!station.currentSong) station.currentSong = { thumbnail: '/assets/notes.png' };
-							if (station.type == 'official') _this.stations.official.push(station);
-							else _this.stations.community.push(station);
-						});
+			auth.getStatus((authenticated, role, username, userId) => {
+				_this.socket = _this.$parent.socket;
+				_this.socket.emit("stations.index", data => {
+					if (data.status === "success")  data.stations.forEach(station => {
+						if (!station.currentSong) station.currentSong = { thumbnail: '/assets/notes.png' };
+						console.log(station.privacy);
+						if (station.privacy !== 'public') {
+							console.log(123);
+							station.class = {'station-red': true}
+						} else if (station.type === 'community') {
+							if (station.owner === userId) {
+								station.class = {'station-blue': true}
+							}
+						}
+						if (station.type == 'official') _this.stations.official.push(station);
+						else _this.stations.community.push(station);
 					});
-					_this.socket.emit("apis.joinRoom", 'home', () => {});
-					_this.socket.on('event:stations.created', station => {
-						if (!station.currentSong) station.currentSong = {};
-						_this.stations[station.type].push(station);
-					});
-					clearInterval(socketInterval);
-				}
-			}, 100);
+				});
+				_this.socket.emit("apis.joinRoom", 'home', () => {});
+				_this.socket.on('event:stations.created', station => {
+					console.log("CREATED!!!", station);
+					if (!station.currentSong) station.currentSong = {thumbnail: '/assets/notes.png'};
+					if (station.privacy !== 'public') {
+						station.class = {'station-red': true}
+					} else if (station.type === 'community') {
+						if (station.owner === userId) {
+							station.class = {'station-blue': true}
+						}
+					}
+					_this.stations[station.type].push(station);
+				});
+			});
 		},
 		methods: {
 			toggleModal: function (type) {
@@ -89,7 +103,6 @@
 </script>
 
 <style lang="scss">
-
 	@import 'theme.scss';
 
 	* { box-sizing: border-box; }
@@ -125,6 +138,24 @@
 		}
 	}
 
+	.ccs-button {
+		cursor: pointer;
+		transition: .25s ease color;
+		font-size: 30px;
+	}
+
+	.ccs-button:hover {
+		color: #03a9f4;
+	}
+
+	.station-blue {
+		outline: 5px solid #03a9f4;
+	}
+
+	.station-red {
+		outline: 5px solid #f45703;
+	}
+
 	.label {
 		display: flex;
 	}

+ 2 - 2
frontend/main.js

@@ -20,8 +20,8 @@ let _this = this;
 lofig.folder = '../config/default.json';
 lofig.get('serverDomain', function(res) {
 	let socket = window.socket = io(res);
-	socket.on("ready", (status, role, username) => {
-		auth.data(status, role, username);
+	socket.on("ready", (status, role, username, userId) => {
+		auth.data(status, role, username, userId);
 	});
 });
 

+ 1 - 1
frontend/package.json

@@ -33,7 +33,7 @@
   "dependencies": {
     "node-sass": "^3.13.0",
     "vue": "^1.0.26",
-    "vue-roaster": "^1.0.8",
+    "vue-roaster": "^1.0.9",
     "vue-router": "^0.7.13"
   }
 }

+ 4 - 0
frontend/theme.scss

@@ -1,4 +1,8 @@
 // Change any Bulma default variables here
 $blue: #03A9F4;
 
+.is-primary {
+	background-color: $blue !important;
+}
+
 @import '~bulma';