فهرست منبع

refactor: Access config store directly

Owen Diffey 1 سال پیش
والد
کامیت
8a5f33f9ff
28فایلهای تغییر یافته به همراه160 افزوده شده و 194 حذف شده
  1. 4 4
      frontend/src/App.vue
  2. 1 1
      frontend/src/classes/SocketHandler.class.ts
  3. 8 8
      frontend/src/components/MainFooter.vue
  4. 9 8
      frontend/src/components/MainHeader.vue
  5. 3 2
      frontend/src/components/Modal.spec.ts
  6. 3 5
      frontend/src/components/Modal.vue
  7. 4 2
      frontend/src/components/PlaylistItem.vue
  8. 1 1
      frontend/src/components/PlaylistTabBase.vue
  9. 5 8
      frontend/src/components/Request.vue
  10. 1 1
      frontend/src/components/__snapshots__/Modal.spec.ts.snap
  11. 5 6
      frontend/src/components/modals/EditPlaylist/Tabs/AddSongs.vue
  12. 3 2
      frontend/src/components/modals/EditPlaylist/Tabs/ImportPlaylists.vue
  13. 3 3
      frontend/src/components/modals/EditSong/Tabs/Songs.vue
  14. 2 1
      frontend/src/components/modals/EditSong/Tabs/Youtube.vue
  15. 4 2
      frontend/src/components/modals/Login.vue
  16. 3 5
      frontend/src/components/modals/ManageStation/Settings.vue
  17. 8 7
      frontend/src/components/modals/Register.vue
  18. 7 11
      frontend/src/components/modals/RemoveAccount.vue
  19. 1 1
      frontend/src/keyboardShortcuts.ts
  20. 2 2
      frontend/src/main.ts
  21. 3 1
      frontend/src/pages/Admin/Songs/index.vue
  22. 9 20
      frontend/src/pages/Home.vue
  23. 4 6
      frontend/src/pages/Settings/Tabs/Security.vue
  24. 3 2
      frontend/src/pages/Station/Sidebar/index.vue
  25. 18 19
      frontend/src/pages/Station/index.vue
  26. 41 57
      frontend/src/stores/config.ts
  27. 1 1
      frontend/src/stores/modals.ts
  28. 4 8
      frontend/src/stores/userAuth.ts

+ 4 - 4
frontend/src/App.vue

@@ -116,7 +116,7 @@ onMounted(async () => {
 
 		if (!loggedIn.value) {
 			broadcastChannel.value.user_login = new BroadcastChannel(
-				`${configStore.get("cookie")}.user_login`
+				`${configStore.cookie}.user_login`
 			);
 			broadcastChannel.value.user_login.onmessage = res => {
 				if (res.data) {
@@ -126,7 +126,7 @@ onMounted(async () => {
 			};
 
 			broadcastChannel.value.nightmode = new BroadcastChannel(
-				`${configStore.get("cookie")}.nightmode`
+				`${configStore.cookie}.nightmode`
 			);
 			broadcastChannel.value.nightmode.onmessage = res => {
 				changeNightmode(!!res.data);
@@ -234,7 +234,7 @@ onMounted(async () => {
 
 		router.isReady().then(() => {
 			if (
-				configStore.get("githubAuthentication") &&
+				configStore.githubAuthentication &&
 				localStorage.getItem("github_redirect")
 			) {
 				router.push(localStorage.getItem("github_redirect") as string);
@@ -242,7 +242,7 @@ onMounted(async () => {
 			}
 		});
 
-		if (configStore.get("christmas")) {
+		if (configStore.christmas) {
 			christmas.value = true;
 			enableChristmasMode();
 		}

+ 1 - 1
frontend/src/classes/SocketHandler.class.ts

@@ -125,7 +125,7 @@ export default class SocketHandler {
 			this.on("ready", data => {
 				console.log("WS: SOCKET READY", data);
 
-				configStore.setConfig(data.config);
+				configStore.$patch(data.config);
 
 				this.onConnectCbs.temp.forEach(cb => cb());
 				this.onConnectCbs.persist.forEach(cb => cb());

+ 8 - 8
frontend/src/components/MainFooter.vue

@@ -1,22 +1,22 @@
 <script setup lang="ts">
 import { computed } from "vue";
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 
 const configStore = useConfigStore();
+const { footerLinks, sitename } = storeToRefs(configStore);
 
 const filteredFooterLinks = computed(() =>
 	Object.fromEntries(
-		Object.entries(configStore.get("footerLinks")).filter(
+		Object.entries(footerLinks.value).filter(
 			url => !(typeof url[1] === "boolean")
 		)
 	)
 );
 
 const getLink = title =>
-	configStore.get("footerLinks")[
-		Object.keys(configStore.get("footerLinks")).find(
-			key => key.toLowerCase() === title
-		)
+	footerLinks.value[
+		Object.keys(footerLinks.value).find(key => key.toLowerCase() === title)
 	];
 </script>
 
@@ -29,11 +29,11 @@ const getLink = title =>
 				</div>
 				<router-link id="footer-logo" to="/">
 					<img
-						v-if="configStore.get('sitename') === 'Musare'"
+						v-if="sitename === 'Musare'"
 						src="/assets/blue_wordmark.png"
-						:alt="configStore.get('sitename')"
+						:alt="sitename"
 					/>
-					<span v-else>{{ configStore.get("sitename") }}</span>
+					<span v-else>{{ sitename }}</span>
 				</router-link>
 				<div id="footer-links">
 					<a

+ 9 - 8
frontend/src/components/MainHeader.vue

@@ -28,6 +28,9 @@ const broadcastChannel = ref();
 const { socket } = useWebsocketsStore();
 
 const configStore = useConfigStore();
+const { cookie, sitename, registrationDisabled, christmas } =
+	storeToRefs(configStore);
+
 const { loggedIn, username } = storeToRefs(userAuthStore);
 const { logout, hasPermission } = userAuthStore;
 const userPreferencesStore = useUserPreferencesStore();
@@ -67,9 +70,7 @@ onMounted(async () => {
 	onResize();
 	window.addEventListener("resize", onResize);
 
-	broadcastChannel.value = new BroadcastChannel(
-		`${configStore.get("cookie")}.nightmode`
-	);
+	broadcastChannel.value = new BroadcastChannel(`${cookie.value}.nightmode`);
 });
 </script>
 
@@ -81,11 +82,11 @@ onMounted(async () => {
 		<div class="nav-left">
 			<router-link v-if="!hideLogo" class="nav-item is-brand" to="/">
 				<img
-					v-if="configStore.get('sitename') === 'Musare'"
+					v-if="sitename === 'Musare'"
 					src="/assets/white_wordmark.png"
-					:alt="configStore.get('sitename')"
+					:alt="sitename"
 				/>
-				<span v-else>{{ configStore.get("sitename") }}</span>
+				<span v-else>{{ sitename }}</span>
 			</router-link>
 		</div>
 
@@ -158,7 +159,7 @@ onMounted(async () => {
 			<span v-if="!loggedIn && !hideLoggedOut" class="grouped">
 				<a class="nav-item" @click="openModal('login')">Login</a>
 				<a
-					v-if="!configStore.get('registrationDisabled')"
+					v-if="!registrationDisabled"
 					class="nav-item"
 					@click="openModal('register')"
 					>Register</a
@@ -167,7 +168,7 @@ onMounted(async () => {
 		</div>
 
 		<christmas-lights
-			v-if="configStore.get('christmas')"
+			v-if="christmas"
 			:lights="Math.min(Math.max(Math.floor(windowWidth / 175), 5), 15)"
 		/>
 	</nav>

+ 3 - 2
frontend/src/components/Modal.spec.ts

@@ -33,11 +33,12 @@ describe("Modal component", () => {
 	});
 
 	test("christmas lights render if enabled", async () => {
-		const configStore = useConfigStore();
-		configStore.config.christmas = true;
 		const wrapper = await getWrapper(Modal, {
 			shallow: true
 		});
+		const configStore = useConfigStore();
+		configStore.christmas = true;
+		await flushPromises();
 		expect(
 			wrapper.findComponent({ name: "ChristmasLights" }).exists()
 		).toBeTruthy();

+ 3 - 5
frontend/src/components/Modal.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { defineAsyncComponent } from "vue";
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 import { useModalsStore } from "@/stores/modals";
 
@@ -14,6 +15,7 @@ defineProps({
 });
 
 const configStore = useConfigStore();
+const { christmas } = storeToRefs(configStore);
 const { closeCurrentModal } = useModalsStore();
 </script>
 
@@ -38,11 +40,7 @@ const { closeCurrentModal } = useModalsStore();
 					>highlight_off</span
 				>
 				<Transition>
-					<christmas-lights
-						v-if="configStore.get('christmas')"
-						small
-						:lights="5"
-					/>
+					<christmas-lights v-if="christmas" small :lights="5" />
 				</Transition>
 			</header>
 			<section class="modal-card-body">

+ 4 - 2
frontend/src/components/PlaylistItem.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import { defineAsyncComponent, computed } from "vue";
+import { storeToRefs } from "pinia";
 import utils from "@/utils";
 import { useConfigStore } from "@/stores/config";
 
@@ -13,6 +14,7 @@ const props = defineProps({
 });
 
 const configStore = useConfigStore();
+const { sitename } = storeToRefs(configStore);
 
 const totalLength = playlist => {
 	let length = 0;
@@ -50,8 +52,8 @@ const playlistLength = computed(
 				<span v-if="showOwner"
 					><a
 						v-if="playlist.createdBy === 'Musare'"
-						:title="configStore.get('sitename')"
-						>{{ configStore.get("sitename") }}</a
+						:title="sitename"
+						>{{ sitename }}</a
 					><user-link v-else :user-id="playlist.createdBy" />
 				</span>

+ 1 - 1
frontend/src/components/PlaylistTabBase.vue

@@ -110,7 +110,7 @@ const excludedYoutubeIds = computed(() => {
 
 	if (
 		autorequestDisallowRecentlyPlayedEnabled &&
-		configStore.get("experimental.station_history")
+		configStore.experimental.station_history
 	) {
 		history.value.forEach((historyItem, index) => {
 			if (index < autorequestDisallowRecentlyPlayedNumber)

+ 5 - 8
frontend/src/components/Request.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { defineAsyncComponent, ref, computed, onMounted, watch } from "vue";
 import Toast from "toasters";
+import { storeToRefs } from "pinia";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useConfigStore } from "@/stores/config";
 import { useStationStore } from "@/stores/station";
@@ -34,6 +35,7 @@ const { soundcloudDirect, addToQueue: soundcloudAddToQueue } =
 
 const { socket } = useWebsocketsStore();
 const configStore = useConfigStore();
+const { sitename, experimental } = storeToRefs(configStore);
 const stationStore = useStationStore();
 const manageStationStore = useManageStationStore({
 	modalUuid: props.modalUuid
@@ -194,7 +196,7 @@ onMounted(async () => {
 			<div class="tab" v-show="tab === 'songs'">
 				<div class="musare-songs">
 					<label class="label">
-						Search for a song on {{ configStore.get("sitename") }}
+						Search for a song on {{ sitename }}
 					</label>
 					<div class="control is-grouped input-with-button">
 						<p class="control is-expanded">
@@ -287,9 +289,7 @@ onMounted(async () => {
 
 				<div
 					class="youtube-search"
-					v-if="
-						!configStore.get('experimental.disable_youtube_search')
-					"
+					v-if="!experimental.disable_youtube_search"
 				>
 					<label class="label"> Search for a song on YouTube </label>
 					<div class="control is-grouped input-with-button">
@@ -362,10 +362,7 @@ onMounted(async () => {
 					</div>
 				</div>
 
-				<div
-					v-if="configStore.get('experimental.soundcloud')"
-					class="soundcloud-direct"
-				>
+				<div v-if="experimental.soundcloud" class="soundcloud-direct">
 					<label class="label">
 						Add a SoundCloud song from a URL
 					</label>

+ 1 - 1
frontend/src/components/__snapshots__/Modal.spec.ts.snap

@@ -1,4 +1,4 @@
-// Vitest Snapshot v1
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
 exports[`Modal component > renders slots 1`] = `
 "<div class=\\"modal is-active\\">

+ 5 - 6
frontend/src/components/modals/EditPlaylist/Tabs/AddSongs.vue

@@ -21,6 +21,7 @@ const props = defineProps({
 });
 
 const configStore = useConfigStore();
+const { sitename, experimental } = storeToRefs(configStore);
 const editPlaylistStore = useEditPlaylistStore({ modalUuid: props.modalUuid });
 const { playlist } = storeToRefs(editPlaylistStore);
 
@@ -118,9 +119,7 @@ watch(
 <template>
 	<div class="youtube-tab section">
 		<div>
-			<label class="label">
-				Search for a song on {{ configStore.get("sitename") }}</label
-			>
+			<label class="label"> Search for a song on {{ sitename }}</label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">
 					<input
@@ -207,7 +206,7 @@ watch(
 			</p>
 		</div>
 
-		<div v-if="!configStore.get('experimental.disable_youtube_search')">
+		<div v-if="!experimental.disable_youtube_search">
 			<label class="label"> Search for a song from YouTube </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">
@@ -279,7 +278,7 @@ watch(
 			</div>
 		</div>
 
-		<template v-if="configStore.get('experimental.soundcloud')">
+		<template v-if="experimental.soundcloud">
 			<label class="label"> Add a SoundCloud song from a URL </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">
@@ -302,7 +301,7 @@ watch(
 			</div>
 		</template>
 
-		<template v-if="configStore.get('experimental.spotify')">
+		<template v-if="experimental.spotify">
 			<label class="label"> Add a Spotify song from a URL </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">

+ 3 - 2
frontend/src/components/modals/EditPlaylist/Tabs/ImportPlaylists.vue

@@ -17,6 +17,7 @@ const props = defineProps({
 const { socket } = useWebsocketsStore();
 
 const configStore = useConfigStore();
+const { experimental } = storeToRefs(configStore);
 const editPlaylistStore = useEditPlaylistStore({ modalUuid: props.modalUuid });
 const { playlist } = storeToRefs(editPlaylistStore);
 
@@ -244,7 +245,7 @@ const importMusarePlaylistFile = () => {
 			</p>
 		</div>
 
-		<template v-if="configStore.get('experimental.soundcloud')">
+		<template v-if="experimental.soundcloud">
 			<label class="label"> Import songs from SoundCloud playlist </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">
@@ -268,7 +269,7 @@ const importMusarePlaylistFile = () => {
 			</div>
 		</template>
 
-		<template v-if="configStore.get('experimental.spotify')">
+		<template v-if="experimental.spotify">
 			<label class="label"> Import songs from Spotify playlist </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">

+ 3 - 3
frontend/src/components/modals/EditSong/Tabs/Songs.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { defineAsyncComponent, onMounted } from "vue";
 
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 import { useEditSongStore } from "@/stores/editSong";
 
@@ -16,6 +17,7 @@ const props = defineProps({
 });
 
 const configStore = useConfigStore();
+const { sitename } = storeToRefs(configStore);
 const { form } = useEditSongStore({ modalUuid: props.modalUuid });
 
 const {
@@ -33,9 +35,7 @@ onMounted(async () => {
 
 <template>
 	<div class="musare-songs-tab">
-		<label class="label">
-			Search for a song on {{ configStore.get("sitename") }}
-		</label>
+		<label class="label"> Search for a song on {{ sitename }} </label>
 		<div class="control is-grouped input-with-button">
 			<p class="control is-expanded">
 				<input

+ 2 - 1
frontend/src/components/modals/EditSong/Tabs/Youtube.vue

@@ -15,6 +15,7 @@ const props = defineProps({
 });
 
 const configStore = useConfigStore();
+const { experimental } = storeToRefs(configStore);
 const editSongStore = useEditSongStore({ modalUuid: props.modalUuid });
 
 const { form, newSong } = storeToRefs(editSongStore);
@@ -57,7 +58,7 @@ const selectSong = (youtubeId, result = null) => {
 			</p>
 		</div>
 
-		<div v-if="!configStore.get('experimental.disable_youtube_search')">
+		<div v-if="!experimental.disable_youtube_search">
 			<label class="label"> Search for a song from YouTube </label>
 			<div class="control is-grouped input-with-button">
 				<p class="control is-expanded">

+ 4 - 2
frontend/src/components/modals/Login.vue

@@ -2,6 +2,7 @@
 import { defineAsyncComponent, ref } from "vue";
 import { useRoute } from "vue-router";
 import Toast from "toasters";
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 import { useUserAuthStore } from "@/stores/userAuth";
 import { useModalsStore } from "@/stores/modals";
@@ -18,6 +19,7 @@ const password = ref({
 const passwordElement = ref();
 
 const configStore = useConfigStore();
+const { githubAuthentication, registrationDisabled } = storeToRefs(configStore);
 const { login } = useUserAuthStore();
 
 const { openModal, closeCurrentModal } = useModalsStore();
@@ -141,7 +143,7 @@ const githubRedirect = () => {
 						Login
 					</button>
 					<a
-						v-if="configStore.get('githubAuthentication')"
+						v-if="githubAuthentication"
 						class="button is-github"
 						:href="configStore.urls.api + '/auth/github/authorize'"
 						@click="githubRedirect()"
@@ -157,7 +159,7 @@ const githubRedirect = () => {
 				</div>
 
 				<p
-					v-if="!configStore.get('registrationDisabled')"
+					v-if="!registrationDisabled"
 					class="content-box-optional-helper"
 				>
 					<a @click="changeToRegisterModal()">

+ 3 - 5
frontend/src/components/modals/ManageStation/Settings.vue

@@ -19,6 +19,7 @@ const props = defineProps({
 const { socket } = useWebsocketsStore();
 
 const configStore = useConfigStore();
+const { experimental } = storeToRefs(configStore);
 
 const manageStationStore = useManageStationStore({
 	modalUuid: props.modalUuid
@@ -326,9 +327,7 @@ watch(station, value => {
 
 						<div
 							class="small-section"
-							v-if="
-								configStore.get('experimental.station_history')
-							"
+							v-if="experimental.station_history"
 						>
 							<label class="label"
 								>Autorequest disallow recent</label
@@ -352,8 +351,7 @@ watch(station, value => {
 							v-if="
 								inputs[
 									'requestsAutorequestDisallowRecentlyPlayedEnabled'
-								].value &&
-								configStore.get('experimental.station_history')
+								].value && experimental.station_history
 							"
 							class="small-section"
 						>

+ 8 - 7
frontend/src/components/modals/Register.vue

@@ -2,6 +2,7 @@
 import { defineAsyncComponent, ref, watch, onMounted } from "vue";
 import { useRoute } from "vue-router";
 import Toast from "toasters";
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 import { useUserAuthStore } from "@/stores/userAuth";
 import { useModalsStore } from "@/stores/modals";
@@ -40,6 +41,8 @@ const passwordElement = ref();
 const { register } = useUserAuthStore();
 
 const configStore = useConfigStore();
+const { registrationDisabled, recaptcha, githubAuthentication } =
+	storeToRefs(configStore);
 const { openModal, closeCurrentModal } = useModalsStore();
 
 const submitModal = () => {
@@ -139,17 +142,17 @@ watch(
 );
 
 onMounted(async () => {
-	if (configStore.get("registrationDisabled")) {
+	if (registrationDisabled.value) {
 		new Toast("Registration is disabled.");
 		closeCurrentModal();
 	}
 
-	if (configStore.get("recaptcha.enabled")) {
+	if (recaptcha.value.enabled) {
 		const recaptchaScript = document.createElement("script");
 		recaptchaScript.onload = () => {
 			grecaptcha.ready(() => {
 				grecaptcha
-					.execute(configStore.get("recaptcha.key"), {
+					.execute(recaptcha.value.key, {
 						action: "login"
 					})
 					.then(token => {
@@ -160,9 +163,7 @@ onMounted(async () => {
 
 		recaptchaScript.setAttribute(
 			"src",
-			`https://www.google.com/recaptcha/api.js?render=${configStore.get(
-				"recaptcha.key"
-			)}`
+			`https://www.google.com/recaptcha/api.js?render=${recaptcha.value.key}`
 		);
 		document.head.appendChild(recaptchaScript);
 	}
@@ -269,7 +270,7 @@ onMounted(async () => {
 						Register
 					</button>
 					<a
-						v-if="configStore.get('githubAuthentication')"
+						v-if="githubAuthentication"
 						class="button is-github"
 						:href="configStore.urls.api + '/auth/github/authorize'"
 						@click="githubRedirect()"

+ 7 - 11
frontend/src/components/modals/RemoveAccount.vue

@@ -2,6 +2,7 @@
 import { defineAsyncComponent, ref, onMounted } from "vue";
 import { useRoute } from "vue-router";
 import Toast from "toasters";
+import { storeToRefs } from "pinia";
 import { useConfigStore } from "@/stores/config";
 import { useSettingsStore } from "@/stores/settings";
 import { useWebsocketsStore } from "@/stores/websockets";
@@ -18,6 +19,7 @@ const props = defineProps({
 });
 
 const configStore = useConfigStore();
+const { cookie, githubAuthentication, messages } = storeToRefs(configStore);
 const settingsStore = useSettingsStore();
 const route = useRoute();
 
@@ -91,9 +93,7 @@ const remove = () =>
 	socket.dispatch("users.remove", res => {
 		if (res.status === "success") {
 			return socket.dispatch("users.logout", () => {
-				document.cookie = `${configStore.get(
-					"cookie"
-				)}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
+				document.cookie = `${cookie.value}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
 				closeCurrentModal();
 				window.location.href = "/";
 			});
@@ -157,8 +157,7 @@ onMounted(async () => {
 				id="password-linked"
 				v-if="
 					step === 'confirm-identity' &&
-					(isPasswordLinked ||
-						!configStore.get('githubAuthentication'))
+					(isPasswordLinked || !githubAuthentication)
 				"
 			>
 				<h2 class="content-box-title">Enter your password</h2>
@@ -217,7 +216,7 @@ onMounted(async () => {
 			<div
 				class="content-box"
 				v-else-if="
-					configStore.get('githubAuthentication') &&
+					githubAuthentication &&
 					isGithubLinked &&
 					step === 'confirm-identity'
 				"
@@ -242,10 +241,7 @@ onMounted(async () => {
 
 			<div
 				class="content-box"
-				v-if="
-					configStore.get('githubAuthentication') &&
-					step === 'relink-github'
-				"
+				v-if="githubAuthentication && step === 'relink-github'"
 			>
 				<h2 class="content-box-title">Re-link GitHub</h2>
 				<p class="content-box-description">
@@ -281,7 +277,7 @@ onMounted(async () => {
 			>
 				<h2 class="content-box-title">Remove your account</h2>
 				<p class="content-box-description">
-					{{ configStore.get("messages.accountRemoval") }}
+					{{ messages.accountRemoval }}
 				</p>
 
 				<div class="content-box-inputs">

+ 1 - 1
frontend/src/keyboardShortcuts.ts

@@ -35,7 +35,7 @@ const lib = {
 			? shortcuts[name].shift
 			: false;
 		const configStore = useConfigStore();
-		const overrides = configStore.get("shortcutOverrides");
+		const overrides = configStore.shortcutOverrides;
 		if (overrides && overrides[name])
 			shortcuts[name] = Object.assign(shortcuts[name], overrides[name]);
 		lib.remakeShortcutsArray();

+ 2 - 2
frontend/src/main.ts

@@ -18,7 +18,7 @@ import AppComponent from "./App.vue";
 
 const handleMetadata = attrs => {
 	const configStore = useConfigStore();
-	document.title = `${configStore.get("sitename")} | ${attrs.title}`;
+	document.title = `${configStore.sitename} | ${attrs.title}`;
 };
 
 const app = createApp(AppComponent);
@@ -403,7 +403,7 @@ createSocket().then(async socket => {
 		});
 	});
 
-	if (configStore.get("experimental.media_session")) ms.init();
+	if (configStore.experimental.media_session) ms.init();
 
 	app.mount("#root");
 });

+ 3 - 1
frontend/src/pages/Admin/Songs/index.vue

@@ -2,6 +2,7 @@
 import { defineAsyncComponent, ref, onMounted } from "vue";
 import { useRoute } from "vue-router";
 import Toast from "toasters";
+import { storeToRefs } from "pinia";
 import { useWebsocketsStore } from "@/stores/websockets";
 import { useConfigStore } from "@/stores/config";
 import { useLongJobsStore } from "@/stores/longJobs";
@@ -33,6 +34,7 @@ const { setJob } = useLongJobsStore();
 const { socket } = useWebsocketsStore();
 
 const configStore = useConfigStore();
+const { experimental } = storeToRefs(configStore);
 
 const { hasPermission } = useUserAuthStore();
 
@@ -551,7 +553,7 @@ onMounted(() => {
 				</button>
 				<button
 					v-if="
-						configStore.get('experimental.spotify') &&
+						experimental.spotify &&
 						(hasPermission('songs.create') ||
 							hasPermission('songs.update'))
 					"

+ 9 - 20
frontend/src/pages/Home.vue

@@ -38,6 +38,7 @@ const userAuthStore = useUserAuthStore();
 const route = useRoute();
 const router = useRouter();
 
+const { sitename, registrationDisabled } = storeToRefs(configStore);
 const { loggedIn, userId } = storeToRefs(userAuthStore);
 const { hasPermission } = userAuthStore;
 
@@ -376,14 +377,12 @@ onBeforeUnmount(() => {
 				<div class="content-container">
 					<div class="content">
 						<img
-							v-if="configStore.get('sitename') === 'Musare'"
+							v-if="sitename === 'Musare'"
 							src="/assets/white_wordmark.png"
-							:alt="configStore.get('sitename')"
+							:alt="sitename"
 							class="logo"
 						/>
-						<span v-else class="logo">{{
-							configStore.get("sitename")
-						}}</span>
+						<span v-else class="logo">{{ sitename }}</span>
 						<div v-if="!loggedIn" class="buttons">
 							<button
 								class="button login"
@@ -392,7 +391,7 @@ onBeforeUnmount(() => {
 								{{ t("Login") }}
 							</button>
 							<button
-								v-if="!configStore.get('registrationDisabled')"
+								v-if="!registrationDisabled"
 								class="button register"
 								@click="openModal('register')"
 							>
@@ -529,16 +528,8 @@ onBeforeUnmount(() => {
 														element.type ===
 														'official'
 													"
-													:title="
-														configStore.get(
-															'sitename'
-														)
-													"
-													>{{
-														configStore.get(
-															"sitename"
-														)
-													}}</span
+													:title="sitename"
+													>{{ sitename }}</span
 												>
 												<user-link
 													v-else
@@ -805,10 +796,8 @@ onBeforeUnmount(() => {
 									<span class="host">
 										<span
 											v-if="station.type === 'official'"
-											:title="configStore.get('sitename')"
-											>{{
-												configStore.get("sitename")
-											}}</span
+											:title="sitename"
+											>{{ sitename }}</span
 										>
 										<user-link
 											v-else

+ 4 - 6
frontend/src/pages/Settings/Tabs/Security.vue

@@ -16,6 +16,7 @@ const QuickConfirm = defineAsyncComponent(
 );
 
 const configStore = useConfigStore();
+const { githubAuthentication, sitename } = storeToRefs(configStore);
 const settingsStore = useSettingsStore();
 const userAuthStore = useUserAuthStore();
 
@@ -212,10 +213,10 @@ watch(validation, newValidation => {
 			<div class="section-margin-bottom" />
 		</div>
 
-		<div v-if="!isGithubLinked && configStore.get('githubAuthentication')">
+		<div v-if="!isGithubLinked && githubAuthentication">
 			<h4 class="section-title">Link your GitHub account</h4>
 			<p class="section-description">
-				Link your {{ configStore.get("sitename") }} account with GitHub
+				Link your {{ sitename }} account with GitHub
 			</p>
 
 			<hr class="section-horizontal-rule" />
@@ -243,10 +244,7 @@ watch(validation, newValidation => {
 
 			<div class="row">
 				<quick-confirm
-					v-if="
-						isPasswordLinked &&
-						configStore.get('githubAuthentication')
-					"
+					v-if="isPasswordLinked && githubAuthentication"
 					@confirm="unlinkPassword()"
 				>
 					<a class="button is-danger">

+ 3 - 2
frontend/src/pages/Station/Sidebar/index.vue

@@ -23,6 +23,7 @@ const stationStore = useStationStore();
 
 const { tab, showTab } = useTabQueryHandler("queue");
 
+const { experimental } = storeToRefs(configStore);
 const { loggedIn } = storeToRefs(userAuthStore);
 const { station, userCount } = storeToRefs(stationStore);
 const { hasPermission } = stationStore;
@@ -88,7 +89,7 @@ onMounted(() => {
 				Request
 			</button>
 			<button
-				v-if="configStore.get('experimental.station_history')"
+				v-if="experimental.station_history"
 				class="button is-default"
 				:class="{ selected: tab === 'history' }"
 				@click="showTab('history')"
@@ -105,7 +106,7 @@ onMounted(() => {
 			sector="station"
 		/>
 		<History
-			v-if="configStore.get('experimental.station_history')"
+			v-if="experimental.station_history"
 			class="tab"
 			v-show="tab === 'history'"
 		/>

+ 18 - 19
frontend/src/pages/Station/index.vue

@@ -54,6 +54,7 @@ const router = useRouter();
 
 const { socket } = useWebsocketsStore();
 const configStore = useConfigStore();
+const { experimental, sitename, christmas } = storeToRefs(configStore);
 const stationStore = useStationStore();
 const userAuthStore = useUserAuthStore();
 const userPreferencesStore = useUserPreferencesStore();
@@ -298,7 +299,7 @@ const autoRequestSong = () => {
 	let excludedYoutubeIds = [];
 	if (
 		autorequestDisallowRecentlyPlayedEnabled &&
-		configStore.get("experimental.station_history")
+		experimental.value.station_history
 	) {
 		excludedYoutubeIds = recentlyPlayedYoutubeIds(
 			autorequestDisallowRecentlyPlayedNumber
@@ -634,7 +635,7 @@ const toggleSkipVote = (message?) => {
 	});
 };
 const resumeLocalPlayer = () => {
-	if (configStore.get("experimental.media_session"))
+	if (experimental.value.media_session)
 		updateMediaSessionData(currentSong.value);
 	if (!noSong.value) {
 		playerSeekTo(getTimeElapsed() / 1000 + currentSong.value.skipDuration);
@@ -647,7 +648,7 @@ const resumeLocalStation = () => {
 	if (!stationPaused.value) resumeLocalPlayer();
 };
 const pauseLocalPlayer = () => {
-	if (configStore.get("experimental.media_session"))
+	if (experimental.value.media_session)
 		updateMediaSessionData(currentSong.value);
 	if (!noSong.value) {
 		timeBeforePause.value = getTimeElapsed();
@@ -698,9 +699,7 @@ const youtubeReady = () => {
 					if (isApple.value) {
 						updateLocalPaused(true);
 						new Toast(
-							`Please click play manually to use ${configStore.get(
-								"sitename"
-							)} on iOS.`
+							`Please click play manually to use ${sitename.value} on iOS.`
 						);
 					}
 				},
@@ -873,8 +872,7 @@ const setCurrentSong = data => {
 
 	clearTimeout(window.stationNextSongTimeout);
 
-	if (configStore.get("experimental.media_session"))
-		updateMediaSessionData(_currentSong);
+	if (experimental.value.media_session) updateMediaSessionData(_currentSong);
 
 	startedAt.value = _startedAt;
 	updateStationPaused(_paused);
@@ -1227,8 +1225,6 @@ onMounted(async () => {
 		);
 	}, 1000);
 
-	const experimental = configStore.get("experimental");
-
 	socket.onConnect(() => {
 		console.debug(TAG, "On socked connect start");
 
@@ -1257,12 +1253,16 @@ onMounted(async () => {
 					djs
 				} = res.data;
 
-				if (experimental && experimental.changable_listen_mode) {
-					if (experimental.changable_listen_mode === true)
+				if (experimental.value.changable_listen_mode) {
+					if (experimental.value.changable_listen_mode === true)
 						experimentalChangableListenModeEnabled.value = true;
 					else if (
-						Array.isArray(experimental.changable_listen_mode) &&
-						experimental.changable_listen_mode.indexOf(_id) !== -1
+						Array.isArray(
+							experimental.value.changable_listen_mode
+						) &&
+						experimental.value.changable_listen_mode.indexOf(
+							_id
+						) !== -1
 					)
 						experimentalChangableListenModeEnabled.value = true;
 				}
@@ -1348,7 +1348,7 @@ onMounted(async () => {
 					}
 				});
 
-				if (configStore.get("experimental.station_history"))
+				if (experimental.value.station_history)
 					socket.dispatch("stations.getHistory", _id, res => {
 						if (res.status === "success") {
 							const { history } = res.data;
@@ -1875,7 +1875,7 @@ onMounted(async () => {
 onBeforeUnmount(() => {
 	document.getElementsByTagName("html")[0].style.cssText = "";
 
-	if (configStore.get("experimental.media_session")) {
+	if (experimental.value.media_session) {
 		ms.removeListeners(0);
 		ms.removeMediaSessionData(0);
 	}
@@ -2231,8 +2231,7 @@ onBeforeUnmount(() => {
 								<div
 									id="seeker-bar"
 									:class="{
-										'christmas-seeker':
-											configStore.get('christmas'),
+										'christmas-seeker': christmas,
 										nyan:
 											currentSong &&
 											currentSong.mediaSource ===
@@ -2321,7 +2320,7 @@ onBeforeUnmount(() => {
 								/>
 								<img
 									v-if="
-										configStore.get('christmas') &&
+										christmas &&
 										currentSong &&
 										![
 											'youtube:QH2-TGUlwu4',

+ 41 - 57
frontend/src/stores/config.ts

@@ -2,67 +2,51 @@ import { defineStore } from "pinia";
 
 export const useConfigStore = defineStore("config", {
 	state: (): {
-		config: {
-			cookie: string;
-			sitename: string;
-			recaptcha: {
-				enabled: boolean;
-				key: string;
-			};
-			githubAuthentication: boolean;
-			messages: Record<string, string>;
-			christmas: boolean;
-			footerLinks: Record<string, string | boolean>;
-			shortcutOverrides: Record<string, any>;
-			registrationDisabled: boolean;
-			experimental: {
-				changable_listen_mode: string[];
-				media_session: boolean;
-				disable_youtube_search: boolean;
-				station_history: boolean;
-				soundcloud: boolean;
-				spotify: boolean;
-			};
+		cookie: string;
+		sitename: string;
+		recaptcha: {
+			enabled: boolean;
+			key: string;
+		};
+		githubAuthentication: boolean;
+		messages: Record<string, string>;
+		christmas: boolean;
+		footerLinks: Record<string, string | boolean>;
+		shortcutOverrides: Record<string, any>;
+		registrationDisabled: boolean;
+		experimental: {
+			changable_listen_mode: string[] | boolean;
+			media_session: boolean;
+			disable_youtube_search: boolean;
+			station_history: boolean;
+			soundcloud: boolean;
+			spotify: boolean;
 		};
 	} => ({
-		config: {
-			cookie: "musareSID",
-			sitename: "Musare",
-			recaptcha: {
-				enabled: false,
-				key: ""
-			},
-			githubAuthentication: false,
-			messages: {
-				accountRemoval:
-					"Your account will be deactivated instantly and your data will shortly be deleted by an admin."
-			},
-			christmas: false,
-			footerLinks: {},
-			shortcutOverrides: {},
-			registrationDisabled: false,
-			experimental: {
-				changable_listen_mode: [],
-				media_session: false,
-				disable_youtube_search: false,
-				station_history: false,
-				soundcloud: false,
-				spotify: false
-			}
-		}
-	}),
-	actions: {
-		setConfig(config) {
-			this.config = config;
+		cookie: "musareSID",
+		sitename: "Musare",
+		recaptcha: {
+			enabled: false,
+			key: ""
 		},
-		get(query: string) {
-			let { config } = this;
-			query.split(".").forEach(property => {
-				config = config[property];
-			});
-			return config;
+		githubAuthentication: false,
+		messages: {
+			accountRemoval:
+				"Your account will be deactivated instantly and your data will shortly be deleted by an admin."
+		},
+		christmas: false,
+		footerLinks: {},
+		shortcutOverrides: {},
+		registrationDisabled: false,
+		experimental: {
+			changable_listen_mode: [],
+			media_session: false,
+			disable_youtube_search: false,
+			station_history: false,
+			soundcloud: false,
+			spotify: false
 		}
-	},
+	}),
 	getters: {
 		urls() {
 			const { protocol, host } = document.location;

+ 1 - 1
frontend/src/stores/modals.ts

@@ -38,7 +38,7 @@ export const useModalsStore = defineStore("modals", {
 				if (uuid === _uuid) {
 					if (modal.modal === "register") {
 						const configStore = useConfigStore();
-						if (configStore.get("recaptcha.enabled"))
+						if (configStore.recaptcha.enabled)
 							window.location.reload();
 					}
 					const close = () => {

+ 4 - 8
frontend/src/stores/userAuth.ts

@@ -122,9 +122,7 @@ export const useUserAuthStore = defineStore("userAuth", {
 									if (configStore.urls.host !== "localhost")
 										domain = ` domain=${configStore.urls.host};`;
 
-									document.cookie = `${configStore.get(
-										"cookie"
-									)}=${
+									document.cookie = `${configStore.cookie}=${
 										res.SID
 									}; expires=${date.toUTCString()}; ${domain}${secure}path=/`;
 
@@ -164,12 +162,12 @@ export const useUserAuthStore = defineStore("userAuth", {
 						if (configStore.urls.host !== "localhost")
 							domain = ` domain=${configStore.urls.host};`;
 
-						document.cookie = `${configStore.get("cookie")}=${
+						document.cookie = `${configStore.cookie}=${
 							res.data.SID
 						}; expires=${date.toUTCString()}; ${domain}${secure}path=/`;
 
 						const bc = new BroadcastChannel(
-							`${configStore.get("cookie")}.user_login`
+							`${configStore.cookie}.user_login`
 						);
 						bc.postMessage(true);
 						bc.close();
@@ -190,9 +188,7 @@ export const useUserAuthStore = defineStore("userAuth", {
 				socket.dispatch("users.logout", res => {
 					if (res.status === "success") {
 						const configStore = useConfigStore();
-						document.cookie = `${configStore.get(
-							"cookie"
-						)}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
+						document.cookie = `${configStore.cookie}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;`;
 						window.location.reload();
 						return resolve(true);
 					}