Browse Source

feat(stations): Added theme functionality to EditStation and Homepage

Owen Diffey 5 years ago
parent
commit
bb290d1b1f

+ 22 - 22
README.md

@@ -1,14 +1,14 @@
 
   
-# MusareNode
+# MusicSite
 
 Staging: [![Build Status](https://travis-ci.org/odiffey/MusicSite.svg?branch=staging)](https://travis-ci.org/odiffey/MusicSite)
 
 Experimental: [![Build Status](https://travis-ci.org/odiffey/MusicSite.svg?branch=experimental)](https://travis-ci.org/odiffey/MusicSite)
 
-Built off of [Musare](https://github.com/Musare/MusareNode).
+Built off of [MusicSite](https://github.com/MusicSite/MusicSite).
 
-MusareNode now uses NodeJS, Express, SocketIO and VueJS - among other technologies. We have also implemented the ability to host Musare in [Docker Containers](https://www.docker.com/).
+MusicSite now uses NodeJS, Express, SocketIO and VueJS - among other technologies. We have also implemented the ability to host MusicSite in [Docker Containers](https://www.docker.com/).
 
 A live version of the site can be found at [music.diffey.dev](https://music.diffey.dev)
 
@@ -52,9 +52,9 @@ Standard Installation:
 
 Once you've installed the required tools:
 
-1. `git clone https://github.com/Musare/MusareNode.git`
+1. `git clone https://github.com/odiffey/MusicSiteSite.git`
 
-2. `cd MusareNode`
+2. `cd MusicSite`
 
 3. `cp backend/config/template.json backend/config/default.json`
 
@@ -78,7 +78,7 @@ Once you've installed the required tools:
 |`apis.discogs`|Can be obtained by setting up a [Discogs application](https://www.discogs.com/settings/developers), or you can disable it.|
 |`redis.url`|Should be left alone for Docker, and changed to `redis://localhost:6379/0` for non-Docker.|
 |`redis.password`|Should be the Redis password you either put in your `startRedis.cmd` file for Windows, or `.env` for docker.|
-|`mongo.url`|Needs to have the proper password for the MongoDB musare user, and for non-Docker you need to replace `@musare:27017` with `@localhost:27017`.|
+|`mongo.url`|Needs to have the proper password for the MongoDB music user, and for non-Docker you need to replace `@music:27017` with `@localhost:27017`.|
 |`cookie.domain`|Should be the ip or address you use to access the site, without protocols (http/https), so for example `localhost`.|
 |`cookie.secure`|Should be `true` for SSL connections, and `false` for normal http connections.|
 
@@ -94,7 +94,7 @@ Once you've installed the required tools:
 |`cookie.secure`|Should be `true` for SSL connections, and `false` for normal http connections.|
 |`siteSettings.logo`|Path to the logo image, by default it is `/assets/wordmark.png`.|
 |`siteSettings.siteName`|Should be the name of the site.|
-|`siteSettings.socialLinks`|`github`, `twitter` and `facebook` are set to the official Musare accounts by default, but can be changed.|
+|`siteSettings.socialLinks`|`github`, `twitter` and `facebook` are set to # by default, but can be changed.|
 
 5. Simply `cp .env.example .env` to setup your environment variables.
 
@@ -111,7 +111,7 @@ _Configuration_
 
 To configure docker configure the `.env` file to match your settings in `backend/config/default.json`.  
 The configurable ports will be how you access the services on your machine, or what ports you will need to specify in your nginx files when using proxy_pass. 
-`COMPOSE_PROJECT_NAME` should be a unique name for this installation, especially if you have multiple instances of Musare on the same machine.
+`COMPOSE_PROJECT_NAME` should be a unique name for this installation, especially if you have multiple instances of MusicSite on the same machine.
 `FRONTEND_MODE` should be either `dev` or `prod` (self-explanatory).
 
 1. Build the backend and frontend Docker images (from the main folder)
@@ -124,7 +124,7 @@ The configurable ports will be how you access the services on your machine, or w
 
       In `.env` set the environment variable of `MONGO_ROOT_PASSWORD`.
 
-   2. Set the password for the musare user (the one the backend will use).
+   2. Set the password for the music user (the one the backend will use).
 
       In `.env` set the environment variable of `MONGO_USER_USERNAME` and `MONGO_USER_PASSWORD`.
 
@@ -143,7 +143,7 @@ The configurable ports will be how you access the services on your machine, or w
 
 5) You should now be able to begin development! The backend is auto reloaded when
    you make changes and the frontend is auto compiled and live reloaded by webpack
-   when you make changes. You should be able to access Musare in your local browser
+   when you make changes. You should be able to access MusicSite in your local browser
    at `http://<docker-machine-ip>:8080/` where `<docker-machine-ip>` can be found below:
 
    - Docker for Windows / Mac: This is just `localhost`
@@ -164,7 +164,7 @@ Steps 1-4 are things you only have to do once. The steps to start servers follow
 
 2.  Create a file called `startMongo.cmd` in the main folder with the contents:
 
-        "C:\Program Files\MongoDB\Server\3.2\bin\mongod.exe" --dbpath "D:\Programming\HTML\MusareNode\.database"
+        "C:\Program Files\MongoDB\Server\3.2\bin\mongod.exe" --dbpath "D:\Programming\HTML\MusicSite\.database"
 
     Make sure to adjust your paths accordingly.
 
@@ -180,13 +180,13 @@ Steps 1-4 are things you only have to do once. The steps to start servers follow
 
        `db.createUser({user: 'admin', pwd: 'PASSWORD_HERE', roles: [{role: 'userAdminAnyDatabase', db: 'admin'}]})`
 
-    4. Connect to the Musare database
+    4. Connect to the MusicSite database
 
-       `use musare`
+       `use music`
 
-    5. Create the musare user
+    5. Create the music user
 
-       `db.createUser({user: 'musare', pwd: 'OTHER_PASSWORD_HERE', roles: [{role: 'readWrite', db: 'musare'}]})`
+       `db.createUser({user: 'music', pwd: 'OTHER_PASSWORD_HERE', roles: [{role: 'readWrite', db: 'music'}]})`
 
     6. Exit
 
@@ -220,7 +220,7 @@ Steps 1-4 are things you only have to do once. The steps to start servers follow
 
 ## Extra
 
-Below is a list of helpful tips / solutions we've collected while developing MusareNode.
+Below is a list of helpful tips / solutions we've collected while developing MusicSite.
 
 ### Mounting a non-standard directory in Docker Toolbox on Windows
 
@@ -235,7 +235,7 @@ of the following commands to give Docker Toolbox access to those files.
 
 1. Next we'll want to tell the machine about the folder we want to share.
 
-   `"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" sharedfolder add default --name "d/Projects/MusareNode" --hostpath "D:\Projects\MusareNode" --automount`
+   `"C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" sharedfolder add default --name "d/Projects/MusicSite" --hostpath "D:\Projects\MusicSite" --automount`
 
 1. Now start the machine back up and ssh into it
 
@@ -246,8 +246,8 @@ of the following commands to give Docker Toolbox access to those files.
    ```bash
    sudo tee -a /mnt/sda1/var/lib/boot2docker/profile >/dev/null <<EOF
 
-   mkdir -p /d/Projects/MusareNode
-   mount -t vboxsf -o uid=1000,gid=50 d/Projects/MusareNode /d/Projects/MusareNode
+   mkdir -p /d/Projects/MusicSite
+   mount -t vboxsf -o uid=1000,gid=50 d/Projects/MusicSite /d/Projects/MusicSite
    EOF
    ```
 
@@ -265,7 +265,7 @@ This command will print various variables.
 At the bottom, it will say something similar to `@FOR /f "tokens=*" %i IN ('docker-machine env default') DO @%i`.
 Run this command in your shell. You will have to do this command for every shell you want to run `docker-compose` in (every session).
 
-### Running Musare locally without using Docker
+### Running MusicSite locally without using Docker
 
 1. Install [Redis](http://redis.io/download) and [MongoDB](https://www.mongodb.com/download-center#community)
 
@@ -301,7 +301,7 @@ When setting up you will need to grant yourself the admin role, using the follow
 ```
 docker-compose exec mongo mongo admin
 
-use musare
+use music
 db.auth("MUSAREDBUSER","MUSAREDBPASSWORD")
 db.users.update({username: "USERNAME"}, {$set: {role: "admin"}})
 ```
@@ -312,5 +312,5 @@ We use lerna to add an additional package to either the frontend or the backend.
 
 For example, this is how we would to add the `webpack-bundle-analyser` package as a dev-dependency to the frontend:
 ```
-npx lerna add webpack-bundle-analyser --scope=musare-frontend --dev
+npx lerna add webpack-bundle-analyser --scope=music-frontend --dev
 ```

+ 34 - 3
backend/logic/actions/stations.js

@@ -359,7 +359,8 @@ module.exports = {
 					locked: station.locked,
 					partyMode: station.partyMode,
 					owner: station.owner,
-					privatePlaylist: station.privatePlaylist
+					privatePlaylist: station.privatePlaylist,
+					theme: station.theme
 				};
 				userList[session.socketId] = station._id;
 				next(null, data);
@@ -879,7 +880,8 @@ module.exports = {
 							playlist,
 							genres,
 							blacklistedGenres,
-							currentSong: stations.defaultSong
+							currentSong: stations.defaultSong,
+							theme: 'blue'
 						}, next);
 					});
 				} else if (type === 'community') {
@@ -892,7 +894,8 @@ module.exports = {
 						privacy: 'private',
 						owner: session.userId,
 						queue: [],
-						currentSong: null
+						currentSong: null,
+						theme: 'blue'
 					}, next);
 				}
 			}
@@ -1215,4 +1218,32 @@ module.exports = {
 			return cb({'status': 'success', 'message': 'Succesfully unfavorited station.'});
 		});
 	}),
+
+	/**
+	 * Updates a station's theme
+	 *
+	 * @param session
+	 * @param stationId - the station id
+	 * @param newTheme - the new station theme
+	 * @param cb
+	 */
+	updateTheme: hooks.ownerRequired((session, stationId, newTheme, cb) => {
+		async.waterfall([
+			(next) => {
+				db.models.station.updateOne({_id: stationId}, {$set: {theme: newTheme}}, {runValidators: true}, next);
+			},
+
+			(res, next) => {
+				stations.updateStation(stationId, next);
+			}
+		], async (err) => {
+			if (err) {
+				err = await utils.getError(err);
+				logger.error("STATIONS_UPDATE_THEME", `Updating station "${stationId}" theme to "${newTheme}" failed. "${err}"`);
+				return cb({'status': 'failure', 'message': err});
+			}
+			logger.success("STATIONS_UPDATE_THEME", `Updated station "${stationId}" theme to "${newTheme}" successfully.`);
+			return cb({'status': 'success', 'message': 'Successfully updated the theme.'});
+		});
+	}),
 };

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

@@ -39,5 +39,6 @@ module.exports = {
 	}],
 	owner: { type: String },
 	privatePlaylist: { type: mongoose.Schema.Types.ObjectId },
-	partyMode: { type: Boolean }
+	partyMode: { type: Boolean },
+	theme: { type: String, enum: ["blue", "purple", "teal"], default: "blue" }
 };

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

@@ -273,7 +273,8 @@ export default {
 				privacy: station.privacy,
 				displayName: station.displayName,
 				genres: station.genres,
-				blacklistedGenres: station.blacklistedGenres
+				blacklistedGenres: station.blacklistedGenres,
+				theme: station.theme
 			});
 			this.openModal({
 				sector: "station",

+ 105 - 0
frontend/components/Modals/EditStation.vue

@@ -275,6 +275,55 @@
 						</button>
 					</div>
 				</div>
+				<div>
+					<label class="label">Theme</label>
+					<div
+						@mouseenter="themeDropdownActive = true"
+						@mouseleave="themeDropdownActive = false"
+						class="button-wrapper"
+					>
+						<button
+							v-bind:class="{
+								blue: true,
+								current: editing.theme === 'blue'
+							}"
+							v-if="
+								themeDropdownActive || editing.theme === 'blue'
+							"
+							@click="updateThemeLocal('blue')"
+						>
+							<i class="material-icons">palette</i>
+							Blue
+						</button>
+						<button
+							v-bind:class="{
+								purple: true,
+								current: editing.theme === 'purple'
+							}"
+							v-if="
+								themeDropdownActive ||
+									editing.theme === 'purple'
+							"
+							@click="updateThemeLocal('purple')"
+						>
+							<i class="material-icons">palette</i>
+							Purple
+						</button>
+						<button
+							v-bind:class="{
+								teal: true,
+								current: editing.theme === 'teal'
+							}"
+							v-if="
+								themeDropdownActive || editing.theme === 'teal'
+							"
+							@click="updateThemeLocal('teal')"
+						>
+							<i class="material-icons">palette</i>
+							Teal
+						</button>
+					</div>
+				</div>
 			</div>
 		</template>
 		<template v-slot:footer>
@@ -332,6 +381,7 @@ export default {
 			privacyDropdownActive: false,
 			modeDropdownActive: false,
 			queueLockDropdownActive: false,
+			themeDropdownActive: false,
 			genres: [
 				"Blues",
 				"Country",
@@ -376,6 +426,7 @@ export default {
 				this.updateDescription();
 			if (this.station.privacy !== this.editing.privacy)
 				this.updatePrivacy();
+			if (this.station.theme !== this.editing.theme) this.updateTheme();
 			if (
 				this.station.type === "community" &&
 				this.station.partyMode !== this.editing.partyMode
@@ -684,6 +735,42 @@ export default {
 				});
 			});
 		},
+		updateThemeLocal(theme) {
+			if (this.editing.theme === theme) return;
+			this.editing.theme = theme;
+			this.themeDropdownActive = false;
+		},
+		updateTheme() {
+			this.socket.emit(
+				"stations.updateTheme",
+				this.editing._id,
+				this.editing.theme,
+				res => {
+					if (res.status === "success") {
+						if (this.station)
+							this.station.theme = this.editing.theme;
+						else {
+							this.$parent.stations.forEach((station, index) => {
+								if (station._id === this.editing._id) {
+									this.$parent.stations[
+										index
+									].theme = this.editing.theme;
+									return this.editing.theme;
+								}
+
+								return false;
+							});
+						}
+						return new Toast({
+							content: res.message,
+							timeout: 4000
+						});
+					}
+
+					return new Toast({ content: res.message, timeout: 8000 });
+				}
+			);
+		},
 		deleteStation() {
 			this.socket.emit("stations.remove", this.editing._id, res => {
 				if (res.status === "success")
@@ -1019,6 +1106,15 @@ export default {
 			transition: all 0.3s ease-in-out;
 		}
 
+		&.teal {
+			&.current,
+			&:hover {
+				background-color: $teal;
+			}
+			background-color: rgba($teal, 0.7);
+			transition: all 0.3s ease-in-out;
+		}
+
 		&.blue {
 			&.current,
 			&:hover {
@@ -1037,6 +1133,15 @@ export default {
 			transition: all 0.3s ease-in-out;
 		}
 
+		&.purple {
+			&.current,
+			&:hover {
+				background-color: $purple;
+			}
+			background-color: rgba($purple, 0.7);
+			transition: all 0.3s ease-in-out;
+		}
+
 		&.yellow {
 			&.current,
 			&:hover {

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

@@ -999,7 +999,8 @@ export default {
 						partyMode,
 						owner,
 						privatePlaylist,
-						type
+						type,
+						theme
 					} = res.data;
 
 					this.joinStation({
@@ -1012,7 +1013,8 @@ export default {
 						partyMode,
 						owner,
 						privatePlaylist,
-						type
+						type,
+						theme
 					});
 					const currentSong = res.data.currentSong
 						? res.data.currentSong

+ 2 - 1
frontend/components/Station/StationHeader.vue

@@ -260,7 +260,8 @@ export default {
 				description: this.station.description,
 				privacy: this.station.privacy,
 				displayName: this.station.displayName,
-				locked: this.station.locked
+				locked: this.station.locked,
+				theme: this.station.theme
 			});
 			this.openModal({
 				sector: "station",

+ 37 - 5
frontend/components/pages/Home.vue

@@ -29,7 +29,12 @@
 							name: 'station',
 							params: { id: station.name }
 						}"
-						class="stationCard"
+						:class="{
+							stationCard: true,
+							blueTheme: station.theme === 'blue',
+							purpleTheme: station.theme === 'purple',
+							tealTheme: station.theme === 'teal'
+						}"
 					>
 						<div class="topContent">
 							<div class="albumArt">
@@ -90,8 +95,9 @@
 									<i
 										v-if="station.type === 'official'"
 										class="badge material-icons"
+										title="Official Station"
 									>
-										verified_user
+										check_circle
 									</i>
 								</div>
 								<p class="description">
@@ -462,9 +468,9 @@ html {
 				.badge {
 					position: relative;
 					padding-left: 5px;
-					color: $lime;
-					top: 3px;
-					font-size: 22px;
+					color: $primary-color;
+					top: 5px;
+					font-size: 18px;
 				}
 			}
 			.description {
@@ -546,6 +552,32 @@ html {
 			}
 		}
 	}
+	&.purpleTheme {
+		.topContent .info {
+			.hostedBy .host,
+			.hostedBy .host a,
+			.displayName .badge {
+				color: $purple;
+			}
+		}
+		.bottomBar {
+			background: $purple;
+			box-shadow: inset 0px 2px 4px rgba(103, 12, 100, 0.7);
+		}
+	}
+	&.tealTheme {
+		.topContent .info {
+			.hostedBy .host,
+			.hostedBy .host a,
+			.displayName .badge {
+				color: $teal;
+			}
+		}
+		.bottomBar {
+			background: $teal;
+			box-shadow: inset 0px 2px 4px rgba(20, 160, 122, 0.7);
+		}
+	}
 }
 .stationCard:hover {
 	box-shadow: 0 2px 3px rgba(10, 10, 10, 0.3), 0 0 10px rgba(10, 10, 10, 0.3);