client/src/components/dialogs/ArtistViewer.vue
Artem Anufrij 724f35dfb1
All checks were successful
continuous-integration/drone/push Build is passing
pregress handling for artists
2023-09-19 15:16:23 +02:00

348 lines
9.5 KiB
Vue

<template>
<DialogBase ref="dialogWindow" id="dialogWindow" :title="selectedArtist.name" @canceled="closed" :showFooter="false" :showFullscreenButton="true" :disableXscroll="true" :disableYscroll="true">
<div id="artistViewer">
<div id="header" class="flex-column">
<div id="background" :style="coverBackground" />
<awesome-icon icon="star" size="2x" class="favourite ma4" :class="{ active: isFavourite }" @click="toggleFavourite" />
<awesome-icon icon="play" size="2x" class="keepPlaying ma4 primary-text" @click="playProgress" v-if="selectedArtist.progress" title="Keep playing" />
<h1 @dblclick="dblclick">
{{ selectedArtist.name }}
</h1>
<span id="stats" class="ma-bottom">
<b>{{ artist_tracks.length }}</b> Tracks in
<b>{{ artist_albums.length }}</b> Albums with a duration of
<b>{{ artist_duration }}</b>
</span>
<div id="albumList" class="flex-row showOnMobilePortrait">
<AlbumItem class="ma" :class="{ playing: playingAlbumId == album._id }" v-for="album in selectedArtist.albums" :key="album._id" :item="album" @click="scrollToAlbum(album)" @dblclick="playAlbum(album)" />
</div>
<div id="navigation" class="flex-row center ma-top">
<div class="flex-row grow"></div>
<div class="flex-row">
<button @click="gotoPrevArtist" class="primary ma4" :title="prevArtist.name" :disabled="!prevArtist._id">
<awesome-icon icon="angle-left" class="ma4" />
</button>
<button @click="gotoNextArtist" class="primary ma4" :title="nextArtist.name" :disabled="!nextArtist._id">
<awesome-icon icon="angle-right" class="ma4" />
</button>
</div>
<div class="flex-row grow right center">
<DropDown v-if="$store.getters['user/isAdministrator']" class="hideOnMobile">
<button class="flat pa8-left pa8-right">
<awesome-icon icon="ellipsis-v" />
</button>
<template v-slot:dropdown-content>
<div>
<button @click="uploadNewCover">
<awesome-icon icon="image" />Set new Cover...
</button>
<button @click="resetCover">
<awesome-icon icon="eraser" />Reset Cover
</button>
<hr />
<button @click="mergeArtist">
<awesome-icon icon="compress-alt" />Merge Artists...
</button>
</div>
</template>
</DropDown>
</div>
</div>
</div>
<div class="flex-row overflow-y">
<div id="albumList" class="flex-column hideOnMobilePortrait">
<AlbumItem class="ma-top ma-left ma-right" :class="{ playing: playingAlbumId == album._id }" v-for="album in selectedArtist.albums" :key="album._id" :item="album" :id="album._id" :ref="album._id" @click="scrollToAlbum(album)" @dblclick="playAlbum(album)" />
</div>
<ul id="trackList" class="tracks" :class="{ playing: selectedTrack._id != null }">
<li v-for="track in selectedArtist.tracks" :key="track._id">
<TrackItem :track="track" :ref="track._id" />
</li>
</ul>
</div>
</div>
<ArtistMerge ref="mergeDialog" />
</DialogBase>
</template>
<script>
import ArtistMerge from "./ArtistMerge";
import TrackItem from "../Track";
import { mapGetters } from "vuex";
export default {
mounted() {
if (this.selectedArtist._id) {
this.$refs.dialogWindow.open();
window.addEventListener("keydown", this.keydownListener);
}
},
methods: {
dblclick() {
this.$store.commit("tracks/resetSelectedTrack");
this.$store.commit("radios/resetSelectedRadio");
this.$store.dispatch("tracks/playContainer", this.selectedArtist);
},
gotoTrack() {
if (this.$route.query.play) {
if (!this.selectedTrack._id) {
let track = this.selectedArtist.tracks.find(
(f) => f._id == this.$route.query.play
);
if (track) {
this.$store.dispatch("tracks/play", track);
}
}
}
},
playProgress() {
let track = this.selectedArtist.tracks.find(
(f) => f._id == this.selectedArtist.progress.id
);
if (track) {
this.$store.dispatch("tracks/play", track);
}
},
gotoNextArtist() {
this.$store.dispatch("artists/gotoNextArtist");
},
gotoPrevArtist() {
this.$store.dispatch("artists/gotoPrevArtist");
},
closed() {
if (
window.history.state.back.indexOf("?") == -1 ||
window.history.state.back.startsWith("/search")
) {
this.$router.back();
} else {
this.$store.dispatch("artists/resetSelectedArtist");
}
},
playAlbum(album) {
this.$store.dispatch("tracks/playContainer", album);
},
keydownListener(e) {
if (e.key == "ArrowLeft") {
e.preventDefault();
this.gotoPrevArtist();
}
if (e.key == "ArrowRight") {
e.preventDefault();
this.gotoNextArtist();
}
},
mergeArtist() {
this.$refs.mergeDialog.open(this.selectedArtist);
},
resetCover() {
this.$store.dispatch("artists/resetCover", this.selectedArtist);
},
scrollToAlbum(album) {
let track = album.tracks[0];
let control = this.$refs[track._id];
control[0].scrollFunction(true);
control = this.$refs[album._id];
control[0].scrollFunction();
},
toggleFavourite() {
this.$store.dispatch("user/toggleFavourite", {
itemId: this.selectedArtist._id,
type: "artist",
});
},
uploadNewCover() {
this.$store.dispatch("artists/uploadNewCover", this.selectedArtist);
},
loadUserProgress() {
if (this.selectedTrack.parent._id != this.selectedArtist._id)
this.$store.dispatch("user/getProgress", this.selectedArtist);
}
},
computed: {
...mapGetters({
prevArtist: ["artists/prevArtist"],
nextArtist: ["artists/nextArtist"],
selectedArtist: ["artists/selectedArtist"],
selectedTrack: ["tracks/selectedTrack"],
favourites: ["user/favourites"],
}),
cover() {
let covers = this.selectedArtist.covers;
if (covers.cover512) {
return covers.cover512;
}
return "/static/icons/dummy/artist.svg";
},
coverBackground() {
return "background-image: url('" + this.cover + "')";
},
artist_albums() {
return this.selectedArtist.albums || [];
},
artist_duration() {
let duration = 0;
let hours = 0;
let minutes = 0;
let seconds = 0;
this.selectedArtist.tracks.forEach((track) => {
duration += track.duration;
});
if (duration >= 3600) {
hours = parseInt(duration / 3600);
duration -= hours * 3600;
}
minutes = parseInt(duration / 60);
seconds = parseInt(duration - minutes * 60);
return (
(hours > 0 ? hours + ":" : "") +
(minutes < 10 ? "0" : "") +
minutes +
":" +
(seconds < 10 ? "0" : "") +
seconds
);
},
artist_tracks() {
return this.selectedArtist.tracks || [];
},
isFavourite() {
return (
this.favourites.find((f) => f.itemId == this.selectedArtist._id) !=
undefined
);
},
playingAlbumId() {
if (this.selectedTrack) {
return this.selectedTrack.parent._id;
} else {
return "";
}
},
},
watch: {
selectedArtist(newVal) {
if (newVal._id) {
if (!this.$refs.dialogWindow.visible) {
this.$refs.dialogWindow.open();
window.addEventListener("keydown", this.keydownListener);
}
this.loadUserProgress();
this.gotoTrack();
} else {
if (this.$refs.dialogWindow.visible) {
this.$refs.dialogWindow.close();
}
window.removeEventListener("keydown", this.keydownListener);
}
},
},
components: {
ArtistMerge,
TrackItem,
},
};
</script>
<style scoped>
#artistViewer {
height: 640px;
display: flex;
flex-direction: column;
overflow: hidden;
}
h1,
#stats {
z-index: 1;
text-align: center;
width: 100%;
color: var(--white);
text-shadow: 0 1px 2px black;
}
#artistImage {
width: 512px;
max-height: 256px;
}
#header {
position: relative;
background-color: black;
width: 760px;
max-width: 100%;
}
#albumList {
z-index: 1;
overflow-y: auto;
background-color: var(--white);
}
#albumList::-webkit-scrollbar {
display: none;
}
#albumList .album:last-child {
margin-bottom: 12px;
}
#navigation {
z-index: 2;
background-color: #ffffff40;
border-top: 1px solid #ffffff20;
border-bottom: 1px solid #00000020;
}
#trackList {
z-index: 1;
background-color: var(--white);
}
.album.playing {
box-shadow: 0px 6px 12px #000000a0 !important;
}
.dialog-window.fullscreen #artistViewer,
.dialog-window.fullscreen #header {
width: initial;
height: initial;
}
.dialog-body button {
color: var(--darkgray);
}
.container {
flex-grow: 0;
}
@media (max-width: 480px) {
#albumList {
background-color: initial;
align-self: center;
padding-bottom: 0;
max-width: 100%;
}
}
@media (max-width: 480px),
(max-height: 480px) {
#artistViewer {
height: initial;
}
#trackList {
width: initial;
height: initial;
}
#header {
width: initial;
}
}
@media (max-height: 480px) {}
</style>