Browse Source

Added preference for ActivityWatch integration

Kristian Vos 3 years ago
parent
commit
28a571c5d2

+ 14 - 1
backend/logic/actions/users.js

@@ -739,6 +739,7 @@ export default {
 	 * @param {boolean} preferences.nightmode - whether or not the user is using the night mode theme
 	 * @param {boolean} preferences.autoSkipDisliked - whether to automatically skip disliked songs
 	 * @param {boolean} preferences.activityLogPublic - whether or not a user's activity log can be publicly viewed
+	 * @param {boolean} preferences.activityWatch - whether or not a user is using the ActivityWatch integration
 	 * @param {Function} cb - gets called with the result
 	 */
 	updatePreferences: isLoginRequired(async function updatePreferences(session, preferences, cb) {
@@ -754,7 +755,8 @@ export default {
 								preferences: {
 									nightmode: preferences.nightmode,
 									autoSkipDisliked: preferences.autoSkipDisliked,
-									activityLogPublic: preferences.activityLogPublic
+									activityLogPublic: preferences.activityLogPublic,
+									activityWatch: preferences.activityWatch
 								}
 							}
 						},
@@ -804,6 +806,17 @@ export default {
 						}
 					});
 
+				if (preferences.activityWatch !== user.preferences.activityWatch)
+					ActivitiesModule.runJob("ADD_ACTIVITY", {
+						userId: session.userId,
+						type: "user__toggle_activity_watch",
+						payload: {
+							message: preferences.activityWatch
+								? "Enabled ActivityWatch integration"
+								: "Disabled ActivityWatch integration"
+						}
+					});
+
 				this.log(
 					"SUCCESS",
 					"UPDATE_USER_PREFERENCES",

+ 1 - 0
backend/logic/activities.js

@@ -95,6 +95,7 @@ class _ActivitiesModule extends CoreClass {
 						const spammableActivities = [
 							"user__toggle_nightmode",
 							"user__toggle_autoskip_disliked_songs",
+							"user__toggle_activity_watch",
 							"song__like",
 							"song__unlike",
 							"song__dislike",

+ 1 - 1
backend/logic/db/index.js

@@ -14,7 +14,7 @@ const REQUIRED_DOCUMENT_VERSIONS = {
 	report: 1,
 	song: 3,
 	station: 4,
-	user: 1
+	user: 2
 };
 
 const regex = {

+ 1 - 0
backend/logic/db/schemas/activity.js

@@ -13,6 +13,7 @@ export default {
 			"user__edit_location",
 			"user__toggle_nightmode",
 			"user__toggle_autoskip_disliked_songs",
+			"user__toggle_activity_watch",
 			/** Songs */
 			"song__report",
 			"song__like",

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

@@ -44,7 +44,8 @@ export default {
 		orderOfPlaylists: [{ type: mongoose.Schema.Types.ObjectId }],
 		nightmode: { type: Boolean, default: false, required: true },
 		autoSkipDisliked: { type: Boolean, default: true, required: true },
-		activityLogPublic: { type: Boolean, default: false, required: true }
+		activityLogPublic: { type: Boolean, default: false, required: true },
+		activityWatch: { type: Boolean, default: false, required: true }
 	},
-	documentVersion: { type: Number, default: 1, required: true }
+	documentVersion: { type: Number, default: 2, required: true }
 };

+ 45 - 0
backend/logic/migration/migrations/migration6.js

@@ -0,0 +1,45 @@
+import async from "async";
+
+/**
+ * Migration 6
+ *
+ * Migration for adding activityWatch preference to user object
+ *
+ * @param {object} MigrationModule - the MigrationModule
+ * @returns {Promise} - returns promise
+ */
+export default async function migrate(MigrationModule) {
+	const userModel = await MigrationModule.runJob("GET_MODEL", { modelName: "user" }, this);
+
+	return new Promise((resolve, reject) => {
+		async.waterfall(
+			[
+				next => {
+					this.log("INFO", `Migration 6. Finding users with document version 1.`);
+					userModel.updateMany(
+						{ documentVersion: 1 },
+						{ $set: { documentVersion: 2, "preferences.activityWatch": false } },
+						(err, res) => {
+							if (err) next(err);
+							else {
+								this.log(
+									"INFO",
+									`Migration 6. Matched: ${res.n}, modified: ${res.nModified}, ok: ${res.ok}.`
+								);
+
+								next();
+							}
+						}
+					);
+				}
+			],
+			err => {
+				if (err) {
+					reject(new Error(err));
+				} else {
+					resolve();
+				}
+			}
+		);
+	});
+}

+ 10 - 2
frontend/src/App.vue

@@ -15,6 +15,7 @@ import { mapState, mapActions, mapGetters } from "vuex";
 import Toast from "toasters";
 
 import ws from "./ws";
+import aw from "./aw";
 import keyboardShortcuts from "./keyboardShortcuts";
 
 export default {
@@ -41,7 +42,8 @@ export default {
 			banned: state => state.user.auth.banned,
 			modals: state => state.modalVisibility.modals,
 			currentlyActive: state => state.modalVisibility.currentlyActive,
-			nightmode: state => state.user.preferences.nightmode
+			nightmode: state => state.user.preferences.nightmode,
+			activityWatch: state => state.user.preferences.activityWatch
 		}),
 		...mapGetters({
 			socket: "websockets/getSocket"
@@ -71,6 +73,10 @@ export default {
 		nightmode(nightmode) {
 			if (nightmode) this.enableNightMode();
 			else this.disableNightMode();
+		},
+		activityWatch(activityWatch) {
+			if (activityWatch) aw.enable();
+			else aw.disable();
 		}
 	},
 	async mounted() {
@@ -142,6 +148,7 @@ export default {
 				this.changeAutoSkipDisliked(res.data.autoSkipDisliked);
 				this.changeNightmode(res.data.nightmode);
 				this.changeActivityLogPublic(res.data.activityLogPublic);
+				this.changeActivityWatch(res.data.activityWatch);
 
 				if (this.nightmode) this.enableNightMode();
 				else this.disableNightMode();
@@ -170,7 +177,8 @@ export default {
 		...mapActions("user/preferences", [
 			"changeNightmode",
 			"changeAutoSkipDisliked",
-			"changeActivityLogPublic"
+			"changeActivityLogPublic",
+			"changeActivityWatch"
 		])
 	}
 };

+ 68 - 47
frontend/src/aw.js

@@ -3,6 +3,7 @@ import Toast from "toasters";
 let gotPong = false;
 let pingTries = 0;
 let uuid = null;
+let enabled = false;
 
 // let sendingVideoDataToast = new Toast({
 // 	content: "Sending video data to ActivityWatch.",
@@ -23,67 +24,87 @@ let uuid = null;
 
 export default {
 	sendVideoData(videoData) {
-		this.sendEvent("videoData", videoData);
+		if (enabled) this.sendEvent("videoData", videoData);
 	},
 
 	sendEvent(type, data) {
-		document.dispatchEvent(
-			new CustomEvent("ActivityWatchMusareEvent", {
-				detail: {
-					type,
-					source: uuid,
-					data
+		if (enabled)
+			document.dispatchEvent(
+				new CustomEvent("ActivityWatchMusareEvent", {
+					detail: {
+						type,
+						source: uuid,
+						data
+					}
+				})
+			);
+	},
+
+	enable() {
+		if (!enabled) {
+			uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
+				/[xy]/g,
+				function abc(c) {
+					// eslint-disable-next-line
+					const r = (Math.random() * 16) | 0;
+					// eslint-disable-next-line
+					const v = c == "x" ? r : (r & 0x3) | 0x8;
+					return v.toString(16);
 				}
-			})
-		);
+			);
+
+			document.addEventListener(
+				"ActivityWatchMusareEvent",
+				this.eventListener
+			);
+
+			this.attemptPing();
+
+			enabled = true;
+			console.log("Enabled AW.");
+
+			// setInterval(() => {
+			// }, 1000);
+		}
 	},
 
-	init() {
-		uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
-			/[xy]/g,
-			function abc(c) {
-				// eslint-disable-next-line
-				const r = (Math.random() * 16) | 0;
-				// eslint-disable-next-line
-				const v = c == "x" ? r : (r & 0x3) | 0x8;
-				return v.toString(16);
-			}
+	disable() {
+		document.removeEventListener(
+			"ActivityWatchMusareEvent",
+			this.eventListener
 		);
+		enabled = false;
+		console.log("Disabled AW.");
+	},
 
-		document.addEventListener("ActivityWatchMusareEvent", event => {
-			const data = event.detail;
+	eventListener(event) {
+		const data = event.detail;
 
-			if (data.type === "pong") {
-				gotPong = true;
-				new Toast({
-					content:
-						"Got pong, connected to ActivityWatch Musare extension",
-					timeout: 8000
-				});
-			}
+		if (data.type === "pong") {
+			gotPong = true;
+			new Toast({
+				content:
+					"Got pong, connected to ActivityWatch Musare extension",
+				timeout: 8000
+			});
+		}
+
+		if (data.type === "denied") {
+			new Toast({
+				content:
+					"Another Musare instance is already sending data to ActivityWatch Musare extension. Please only use 1 active tab for stations and editsong.",
+				timeout: 4000
+			});
+		}
 
-			if (data.type === "denied") {
+		if (data.type === "competitor") {
+			if (data.competitor !== uuid)
 				new Toast({
 					content:
-						"Another Musare instance is already sending data to ActivityWatch Musare extension. Please only use 1 active tab for stations and editsong.",
+						"Another Musare instance is trying and failing to send data to the ActivityWatch Musare instance. Please only use 1 active tab for stations and editsong.",
 					timeout: 4000
 				});
-			}
-
-			if (data.type === "competitor") {
-				if (data.competitor !== uuid)
-					new Toast({
-						content:
-							"Another Musare instance is trying and failing to send data to the ActivityWatch Musare instance. Please only use 1 active tab for stations and editsong.",
-						timeout: 4000
-					});
-			}
-		});
-
-		this.attemptPing();
-
-		// setInterval(() => {
-		// }, 1000);
+		}
 	},
 
 	attemptPing() {

+ 1 - 0
frontend/src/components/ActivityItem.vue

@@ -115,6 +115,7 @@ export default {
 				user__edit_location: "place",
 				user__toggle_nightmode: "nightlight_round",
 				user__toggle_autoskip_disliked_songs: "thumb_down_alt",
+				user__toggle_activity_watch: "visibility",
 				/** Songs */
 				song__report: "flag",
 				song__like: "thumb_up_alt",

+ 5 - 3
frontend/src/main.js

@@ -3,7 +3,6 @@ import Vue from "vue";
 import VueTippy, { TippyComponent } from "vue-tippy";
 import VueRouter from "vue-router";
 
-import aw from "@/aw";
 import ws from "@/ws";
 import store from "./store";
 
@@ -175,8 +174,6 @@ lofig.folder = "../config/default.json";
 		}
 	});
 
-	aw.init();
-
 	const websocketsDomain = await lofig.get("websocketsDomain");
 	ws.init(websocketsDomain);
 
@@ -212,6 +209,11 @@ lofig.folder = "../config/default.json";
 			"user/preferences/changeActivityLogPublic",
 			preferences.activityLogPublic
 		);
+
+		store.dispatch(
+			"user/preferences/changeActivityWatch",
+			preferences.activityWatch
+		);
 	});
 
 	router.beforeEach((to, from, next) => {

+ 24 - 5
frontend/src/pages/Settings/tabs/Preferences.vue

@@ -35,6 +35,17 @@
 				<p>Allow my activity log to be viewed publicly</p>
 			</label>
 		</p>
+		<p class="control is-expanded checkbox-control">
+			<input
+				type="checkbox"
+				id="activityWatch"
+				v-model="localActivityWatch"
+			/>
+			<label for="activityWatch">
+				<span></span>
+				<p>Use ActivityWatch integration (requires extension)</p>
+			</label>
+		</p>
 
 		<save-button ref="saveButton" @clicked="saveChanges()" />
 	</div>
@@ -52,14 +63,17 @@ export default {
 		return {
 			localNightmode: false,
 			localAutoSkipDisliked: false,
-			localActivityLogPublic: false
+			localActivityLogPublic: false,
+			localActivityWatch: false
 		};
 	},
 	computed: {
 		...mapState({
 			nightmode: state => state.user.preferences.nightmode,
 			autoSkipDisliked: state => state.user.preferences.autoSkipDisliked,
-			activityLogPublic: state => state.user.preferences.activityLogPublic
+			activityLogPublic: state =>
+				state.user.preferences.activityLogPublic,
+			activityWatch: state => state.user.preferences.activityWatch
 		}),
 		...mapGetters({
 			socket: "websockets/getSocket"
@@ -71,6 +85,7 @@ export default {
 				this.localNightmode = res.data.nightmode;
 				this.localAutoSkipDisliked = res.data.autoSkipDisliked;
 				this.localActivityLogPublic = res.data.activityLogPublic;
+				this.localActivityWatch = res.data.activityWatch;
 			}
 		});
 
@@ -78,6 +93,7 @@ export default {
 			this.localNightmode = preferences.nightmode;
 			this.localAutoSkipDisliked = preferences.autoSkipDisliked;
 			this.localActivityLogPublic = preferences.activityLogPublic;
+			this.localActivityWatch = preferences.activityWatch;
 		});
 	},
 	methods: {
@@ -85,7 +101,8 @@ export default {
 			if (
 				this.localNightmode === this.nightmode &&
 				this.localAutoSkipDisliked === this.autoSkipDisliked &&
-				this.localActivityLogPublic === this.activityLogPublic
+				this.localActivityLogPublic === this.activityLogPublic &&
+				this.localActivityWatch === this.activityWatch
 			) {
 				new Toast({
 					content: "Please make a change before saving.",
@@ -102,7 +119,8 @@ export default {
 				{
 					nightmode: this.localNightmode,
 					autoSkipDisliked: this.localAutoSkipDisliked,
-					activityLogPublic: this.localActivityLogPublic
+					activityLogPublic: this.localActivityLogPublic,
+					activityWatch: this.localActivityWatch
 				},
 				res => {
 					if (res.status !== "success") {
@@ -123,7 +141,8 @@ export default {
 		...mapActions("user/preferences", [
 			"changeNightmode",
 			"changeAutoSkipDisliked",
-			"changeActivityLogPublic"
+			"changeActivityLogPublic",
+			"changeActivityWatch"
 		])
 	}
 };

+ 8 - 1
frontend/src/store/modules/user.js

@@ -220,7 +220,8 @@ const modules = {
 		state: {
 			nightmode: false,
 			autoSkipDisliked: true,
-			activityLogPublic: false
+			activityLogPublic: false,
+			activityWatch: false
 		},
 		actions: {
 			changeNightmode: ({ commit }, nightmode) => {
@@ -231,6 +232,9 @@ const modules = {
 			},
 			changeActivityLogPublic: ({ commit }, activityLogPublic) => {
 				commit("changeActivityLogPublic", activityLogPublic);
+			},
+			changeActivityWatch: ({ commit }, activityWatch) => {
+				commit("changeActivityWatch", activityWatch);
 			}
 		},
 		mutations: {
@@ -242,6 +246,9 @@ const modules = {
 			},
 			changeActivityLogPublic(state, activityLogPublic) {
 				state.activityLogPublic = activityLogPublic;
+			},
+			changeActivityWatch(state, activityWatch) {
+				state.activityWatch = activityWatch;
 			}
 		}
 	}