client/src/App.vue
Artem Anufrij ad6161c40a
All checks were successful
continuous-integration/drone/push Build is passing
show dummy icon inside media info
2023-09-12 20:28:35 +02:00

322 lines
9.3 KiB
Vue

<template>
<div id="app">
<nav
v-show="
$route.path != '/login' &&
$route.path != '/setup' &&
$route.path != '/share'
"
:class="{ slideOverTop: $store.getters.isDialogOpen }"
>
<div>
<router-link to="/" title="Home" class="primary">
<awesome-icon icon="home" />
</router-link>
<router-link to="/albums" title="Albums">
<awesome-icon icon="compact-disc" />
<span class="navigation-title">Albums</span>
</router-link>
<router-link to="/artists" title="Artists">
<awesome-icon icon="user-circle" />
<span class="navigation-title">Artists</span>
</router-link>
<router-link to="/radios" title="Online Radio">
<awesome-icon icon="podcast" />
<span class="navigation-title">Radios</span>
</router-link>
<router-link to="/boxes" title="Boxes">
<awesome-icon icon="film" />
<span class="navigation-title">Videos</span>
</router-link>
<router-link
to="/favourites"
title="Favourites"
v-if="user.favourites.length > 0"
>
<awesome-icon icon="star" />
<span class="navigation-title">Favourites</span>
</router-link>
</div>
<div></div>
<div>
<input
type="search"
@focus="gotoSearchView"
@search="searchTermChanged"
incremental
v-model.lazy="$store.state.search.term"
placeholder="Search…"
autocomplete="one-time-code"
class="hideOnMobile"
/>
<DropDown v-if="user.token">
<template v-slot:default>
<button>
<awesome-icon icon="cog" />
</button>
</template>
<template v-slot:dropdown-content>
<button
v-for="(menuItem, i) in $store.getters.viewMenu"
:key="i"
@click="menuItem.event"
>
<awesome-icon :icon="menuItem.icon" />{{ menuItem.title }}
</button>
<hr v-if="$store.getters.viewMenu.length > 0" />
<button
@click="openDialog('dialogUsers')"
v-if="$store.getters['user/isModerator']"
>
<awesome-icon icon="users" />User management
</button>
<button
@click="openDialog('serverSettings')"
v-if="$store.getters['user/isAdministrator']"
>
<awesome-icon icon="server" />Server settings
</button>
<button
@click="openDialog('sharedItems')"
v-if="$store.getters['user/isAdministrator']"
>
<awesome-icon icon="share" />Shared items
</button>
<button
v-if="$store.getters.isElectron"
@click="openDialog('dialogSettings')"
>
<awesome-icon icon="cog" />Settings
</button>
<hr />
<button
@click="openDialog('audioUploader')"
v-if="!$store.getters['user/isGuest']"
>
<awesome-icon icon="music" />
Upload Audio Files
</button>
<button
@click="openDialog('videoUploader')"
v-if="!$store.getters['user/isGuest']"
>
<awesome-icon icon="video" />
Upload Video Files
</button>
<hr />
<button @click="openDialog('dialogUserProfile')">
<awesome-icon icon="user" />Profile
</button>
<button @click="logout">
<awesome-icon icon="sign-out-alt" />Logout [{{ user.name }}]
</button>
</template>
</DropDown>
<button v-else title="Login" @click="logout">
<awesome-icon icon="sign-in-alt" size="2x" />
</button>
</div>
</nav>
<div id="app-body">
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</div>
<div id="popup-collection">
<PopUp
v-for="popup in $store.state.popups"
:key="popup.time"
:item="popup"
/>
</div>
<Player ref="player" />
<Users ref="dialogUsers" />
<UserProfile ref="dialogUserProfile" />
<ServerSettings ref="serverSettings" />
<DesktopSettings ref="dialogSettings" />
<VideoScreen />
<AudioUploadDialog ref="audioUploader" />
<VideoUploadDialog ref="videoUploader" />
<SharedItems ref="sharedItems" />
</div>
</template>
<script>
import VideoScreen from "./components/dialogs/VideoScreen";
import DropDown from "./components/base-components/DropDown";
import Users from "./components/dialogs/Users";
import UserProfile from "./components/dialogs/UserProfile";
import DesktopSettings from "./components/dialogs/DesktopSettings";
import Player from "./components/Player";
import ServerSettings from "./components/dialogs/ServerSettings";
import AudioUploadDialog from "./components/dialogs/AudioUpload";
import VideoUploadDialog from "./components/dialogs/VideoUpload";
import SharedItems from "./components/dialogs/SharedItems";
import PopUp from "./components/base-components/PopUp";
import { mapGetters } from "vuex";
export default {
name: "app",
data() {
return {
searchTimer: 0,
timeoutHandler: 0,
};
},
created() {
this.login();
this.$store.dispatch("loadClientConfigs").then(() => {
this.$store.dispatch("system/loadLists");
this.$store.dispatch("loadServerInfo");
this.$store.dispatch("loadSystemSettings");
});
},
mounted() {
document.addEventListener("keydown", (e) => {
switch (e.key) {
case "MediaPlay":
this.$refs.player.togglePlaying();
break;
case "MediaTrackNext":
this.$store.dispatch(
"tracks/playNextTo",
this.$store.getters("tracks/selectedTrack")
);
break;
case "MediaTrackPrevious":
this.$store.dispatch(
"tracks/playPrevTo",
this.$store.getters("tracks/selectedTrack")
);
break;
}
});
},
methods: {
pushMediaInfo(track) {
if ("mediaSession" in navigator && track._id) {
navigator.mediaSession.metadata = new MediaMetadata({
title: track.title,
artist: track.parent.artist_name,
album: track.parent.title,
artwork: [
{
src:
track.parent.covers.cover32 || "/static/icons/dummy/album.svg",
sizes: "32x32",
type: "image/png",
},
{
src:
track.parent.covers.cover64 || "/static/icons/dummy/album.svg",
sizes: "64x64",
type: "image/png",
},
{
src:
track.parent.covers.cover128 || "/static/icons/dummy/album.svg",
sizes: "96x96",
type: "image/png",
},
{
src:
track.parent.covers.cover128 || "/static/icons/dummy/album.svg",
sizes: "128x128",
type: "image/png",
},
{
src:
track.parent.covers.cover128 || "/static/icons/dummy/album.svg",
sizes: "256x256",
type: "image/png",
},
],
});
}
},
gotoSearchView() {
if (this.$route.path != "/search") {
let q = this.$store.getters["search/term"];
if (q != "") {
this.$router.push("/search?q=" + q);
} else {
this.$router.push("/search");
}
}
},
searchTermChanged(e) {
clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
let v = e.srcElement.value;
if (!v) {
this.$store.commit("search/setTerm", v);
if (this.$route.query.q) {
this.$router.replace("/search");
}
return;
} else if (v == this.$store.getters["search/term"]) {
return;
}
this.$store.commit("search/setTerm", v);
this.$router.replace("/search?q=" + this.$store.getters["search/term"]);
}, 250);
},
openDialog(name) {
this.$refs[name].open();
},
login() {
let hash = window.location.hash.replace("#/", "");
if (
!hash.startsWith("login") &&
!hash.startsWith("setup") &&
!hash.startsWith("share")
) {
let redirect = encodeURIComponent(hash);
if (redirect) {
this.$router.replace({
path: "login",
query: { redirect: redirect },
});
} else {
this.$router.replace("login");
}
}
},
logout() {
this.$store.dispatch("user/logout");
},
},
computed: {
...mapGetters({
selectedTrack: "tracks/selectedTrack",
user: "user/user",
}),
},
watch: {
selectedTrack(newVal) {
if (newVal._id) {
this.pushMediaInfo(newVal);
}
},
},
components: {
Player,
AudioUploadDialog,
DropDown,
ServerSettings,
DesktopSettings,
Users,
UserProfile,
VideoScreen,
VideoUploadDialog,
SharedItems,
PopUp,
},
};
</script>