From b71f7ebcd89e04cdd5ae9c7d69b28d635c029466 Mon Sep 17 00:00:00 2001 From: Ciaran Ainsworth <ciaranainsworth@outlook.com> Date: Mon, 3 May 2021 16:19:40 +0100 Subject: [PATCH] Add support for all list locations Remove reliability on play button --- front/src/components/audio/ArtistEntries.vue | 117 +++++++++++------- .../components/audio/track/PlayIndicator.vue | 8 ++ front/src/components/favorites/List.vue | 10 +- front/src/components/library/AlbumDetail.vue | 12 +- front/src/components/library/ArtistDetail.vue | 2 +- front/src/style/_main.scss | 1 + front/src/style/components/_button.scss | 2 +- .../src/style/components/_play_indicator.scss | 29 +++++ front/src/style/globals/_channels.scss | 6 + 9 files changed, 129 insertions(+), 58 deletions(-) create mode 100644 front/src/components/audio/track/PlayIndicator.vue create mode 100644 front/src/style/components/_play_indicator.scss diff --git a/front/src/components/audio/ArtistEntries.vue b/front/src/components/audio/ArtistEntries.vue index e2467d03eb..98466d6f49 100644 --- a/front/src/components/audio/ArtistEntries.vue +++ b/front/src/components/audio/ArtistEntries.vue @@ -2,13 +2,16 @@ <div class="artist-entries ui unstackable grid"> <div class="artist-entries row"> <div class="actions one wide left floated column"></div> - <div class="image one wide left floated column"></div> + <div v-if="showArt" class="image one wide left floated column"></div> <div class="content ellipsis two wide left floated column"> <b>{{ labels.title }}</b> </div> - <div class="content ellipsis two wide left floated column"> + <div v-if="showAlbum" class="content ellipsis two wide left floated column"> <b>{{ labels.album }}</b> </div> + <div v-if="showArtist" class="content ellipsis two wide left floated column"> + <b>{{ labels.artist }}</b> + </div> <div class="meta one wide right floated column"></div> <div class="meta one wide right floated column"> <i class="clock icon" style="padding: 0.5rem;" /> @@ -19,56 +22,58 @@ :class="[{active: currentTrack && track.id === currentTrack.id}, 'artist-entry row']" @mouseover="track.hover = true" @mouseleave="track.hover = false" - @dblclick="replacePlay(tracks, index)" + @dblclick="doubleClick(track, index)" @contextmenu.prevent="$refs.playmenu.open()" v-for="(track, index) in tracks" :key="track.id"> <div class="actions one wide left floated column"> - <play-button - v-if="currentTrack && isPlaying && track.id === currentTrack.id" - class="basic circular icon" - :playing="true" - :button-classes="pausedButtonClasses" - :discrete="true" - :icon-only="true" - :track="track" - :tracks="tracks"> - </play-button> - <play-button - v-else-if="currentTrack && !isPlaying && track.id === currentTrack.id" - class="basic circular icon" - :paused="true" - :button-classes="pausedButtonClasses" - :discrete="true" - :icon-only="true" - :track="track" - :tracks="tracks"> - </play-button> - <play-button - v-else-if="track.hover" - class="basic circular icon" - :button-classes="playingButtonClasses" - :discrete="true" :icon-only="true" - :track="track" - :tracks="tracks"> - </play-button> + <play-indicator + v-if="!isLoadingAudio && currentTrack && isPlaying && track.id === currentTrack.id && !track.hover"> + </play-indicator> + <button + v-else-if="currentTrack && isPlaying && track.id === currentTrack.id && track.hover" + class="ui really tiny basic icon button play-button" + @click.prevent.exact="pausePlayback" + > + <i class="pause icon" /> + </button> + <button + v-else-if="currentTrack && !isPlaying && track.id === currentTrack.id && track.hover" + class="ui really tiny basic icon button play-button" + @click.prevent.exact="resumePlayback" + > + <i class="play icon" /> + </button> + <button + v-else-if="track.hover" + class="ui really tiny basic icon button play-button" + @click.prevent.exact="replacePlay(tracks, index)" + > + <i class="play icon" /> + </button> + <span class="trackPosition" v-else-if="showPosition"> + {{ prettyPosition(track.position) }} + </span> </div> - <div class="image one wide left floated column"> + <div v-if="showArt" class="image left floated column"> <img alt="" class="ui artist-track mini image" v-if="track.album && track.album.cover && track.album.cover.urls.original" v-lazy="$store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)"> <img alt="" class="ui artist-track mini image" v-else src="../../assets/audio/default-cover.png"> </div> - <div class="content ellipsis two wide left floated column"> + <div class="content ellipsis left floated column"> <router-link :to="{name: 'library.tracks.detail', params: {id: track.id }}">{{ track.title }}</router-link> </div> - <div class="content ellipsis two wide left floated column"> + <div v-if="showAlbum" class="content ellipsis left floated column"> <router-link :to="{name: 'library.albums.detail', params: {id: track.album.id }}">{{ track.album.title }}</router-link> </div> - <div class="meta one wide right floated column"> + <div v-if="showArtist" class="content ellipsis left floated column"> + <router-link class="artist link" :to="{name: 'library.artists.detail', params: {id: track.artist.id }}">{{ track.artist.name }}</router-link> + </div> + <div class="meta right floated column"> <track-favorite-icon class="tiny" :border="false" :track="track"></track-favorite-icon> </div> - <div class="meta one wide right floated column"> + <div class="meta right floated column"> <human-duration v-if="track.uploads[0] && track.uploads[0].duration" :duration="track.uploads[0].duration"></human-duration> </div> - <div class="one wide right floated column"> + <div class="right floated column"> <play-button id="playmenu" class="play-button basic icon" :dropdown-only="true" :is-playable="track.is_playable" :dropdown-icon-classes="['ellipsis', 'vertical', 'large really discrete']" :track="track"></play-button> </div> </div> @@ -77,28 +82,29 @@ <script> import _ from '@/lodash' -import PlayButton from '@/components/audio/PlayButton' import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon' -import { mapGetters } from "vuex" - +import { mapActions, mapGetters } from "vuex" +import PlayIndicator from '@/components/audio/track/PlayIndicator' +import PlayButton from '@/components/audio/PlayButton' export default { props: { tracks: Array, + showAlbum: {type: Boolean, required: false, default: true}, + showArtist: {type: Boolean, required: false, default: true}, + trackOnly: {type: Boolean, required: false, default: false}, + showPosition: {type: Boolean, required: false, default: false}, + showArt: {type: Boolean, required: false, default: true} }, components: { - PlayButton, TrackFavoriteIcon, - }, - data() { - return { - playingButtonClasses: ['really', 'tiny', 'basic', 'icon', 'button', 'play-button'], - pausedButtonClasses: ['really', 'tiny', 'basic', 'icon', 'button', 'play-button', 'paused'], - } + PlayIndicator, + PlayButton }, computed: { ...mapGetters({ currentTrack: "queue/currentTrack", + isLoadingAudio: "player/isLoadingAudio" }), isPlaying () { @@ -108,11 +114,17 @@ export default { labels() { return { title: this.$pgettext("*/*/*/Noun", "Title"), - album: this.$pgettext("*/*/*/Noun", "Album") + album: this.$pgettext("*/*/*/Noun", "Album"), + artist: this.$pgettext("*/*/*/Noun", "Artist") } } }, methods: { + ...mapActions({ + resumePlayback: "player/resumePlayback", + pausePlayback: "player/pausePlayback", + }), + prettyPosition (position, size) { var s = String(position); while (s.length < (size || 2)) {s = "0" + s;} @@ -124,6 +136,15 @@ export default { this.$store.dispatch('queue/currentIndex', trackIndex) }) }, + doubleClick(track, index) { + if (this.currentTrack && this.isPlaying && track.id === this.currentTrack.id) { + this.pausePlayback() + } else if (this.currentTrack && !this.isPlaying && track.id === this.currentTrack.id) { + this.resumePlayback() + } else { + this.replacePlay(this.tracks, index) + } + } }, created () { this.tracks.forEach((track) => { diff --git a/front/src/components/audio/track/PlayIndicator.vue b/front/src/components/audio/track/PlayIndicator.vue new file mode 100644 index 0000000000..c97b3c1224 --- /dev/null +++ b/front/src/components/audio/track/PlayIndicator.vue @@ -0,0 +1,8 @@ +<template> + <div id="audio-bars"> + <div class="audio-bar"></div> + <div class="audio-bar"></div> + <div class="audio-bar"></div> + <div class="audio-bar"></div> + </div> +</template> \ No newline at end of file diff --git a/front/src/components/favorites/List.vue b/front/src/components/favorites/List.vue index b43ec41f4f..62e50dc142 100644 --- a/front/src/components/favorites/List.vue +++ b/front/src/components/favorites/List.vue @@ -24,7 +24,7 @@ <div class="field"> <label for="favorites-ordering"><translate translate-context="Content/Search/Dropdown.Label/Noun">Ordering</translate></label> <select id="favorites-ordering" class="ui dropdown" v-model="ordering"> - <option v-for="option in orderingOptions" :value="option[0]"> + <option v-for="option in orderingOptions" :value="option[0]" :key="option[0]"> {{ sharedLabels.filters[option[1]] }} </option> </select> @@ -46,7 +46,7 @@ </div> </div> </div> - <track-table v-if="results" :tracks="results.results"></track-table> + <artist-entries :show-artist="true" :show-album="true" v-if="results" :tracks="results.results"></artist-entries> <div class="ui center aligned basic segment"> <pagination v-if="results && results.count > paginateBy" @@ -76,21 +76,21 @@ import axios from "axios" import $ from "jquery" import logger from "@/logging" -import TrackTable from "@/components/audio/track/Table" import RadioButton from "@/components/radios/Button" import Pagination from "@/components/Pagination" import OrderingMixin from "@/components/mixins/Ordering" import PaginationMixin from "@/components/mixins/Pagination" import TranslationsMixin from "@/components/mixins/Translations" import {checkRedirectToLogin} from '@/utils' +import ArtistEntries from '@/components/audio/ArtistEntries' const FAVORITES_URL = "tracks/" export default { mixins: [OrderingMixin, PaginationMixin, TranslationsMixin], components: { - TrackTable, RadioButton, - Pagination + Pagination, + ArtistEntries }, data() { return { diff --git a/front/src/components/library/AlbumDetail.vue b/front/src/components/library/AlbumDetail.vue index 2a41937622..a51adea3e4 100644 --- a/front/src/components/library/AlbumDetail.vue +++ b/front/src/components/library/AlbumDetail.vue @@ -19,7 +19,13 @@ </div> </template> <template v-else> - <album-entries :tracks="object.tracks"></album-entries> + <artist-entries + :tracks="object.tracks" + :show-position="true" + :show-art="false" + :show-album="false" + :show-artist="false"> + </artist-entries> </template> <div class="ui center aligned basic segment"> <pagination @@ -50,7 +56,7 @@ import logger from "@/logging" import LibraryWidget from "@/components/federation/LibraryWidget" import TrackTable from "@/components/audio/track/Table" import ChannelEntries from '@/components/audio/ChannelEntries' -import AlbumEntries from '@/components/audio/AlbumEntries' +import ArtistEntries from '@/components/audio/ArtistEntries' import Pagination from "@/components/Pagination" import PaginationMixin from "@/components/mixins/Pagination" import PlayButton from "@/components/audio/PlayButton" @@ -59,7 +65,7 @@ export default { props: ["object", "libraries", "discs", "isSerie", "artist", "page", "paginateBy", "totalTracks"], components: { LibraryWidget, - AlbumEntries, + ArtistEntries, ChannelEntries, TrackTable, Pagination, diff --git a/front/src/components/library/ArtistDetail.vue b/front/src/components/library/ArtistDetail.vue index cdce0685ec..222a75e869 100644 --- a/front/src/components/library/ArtistDetail.vue +++ b/front/src/components/library/ArtistDetail.vue @@ -19,7 +19,7 @@ <translate translate-context="Content/Artist/Title">New tracks by this artist</translate> </h2> <div class="ui hidden divider"></div> - <artist-entries :tracks="tracks.slice(0,5)"></artist-entries> + <artist-entries :show-position="false" :track-only="true" :tracks="tracks.slice(0,5)"></artist-entries> </section> <section v-if="isLoadingAlbums" class="ui vertical stripe segment"> <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div> diff --git a/front/src/style/_main.scss b/front/src/style/_main.scss index 678205fcae..c0faa0f155 100644 --- a/front/src/style/_main.scss +++ b/front/src/style/_main.scss @@ -48,6 +48,7 @@ $bottom-player-height: 4rem; @import "./components/_user_link.scss"; @import "./components/_volume_control.scss"; @import "./components/_loaders.scss"; +@import "./components/play_indicator.scss"; @import "./pages/_about.scss"; @import "./pages/_admin_account_detail.scss"; diff --git a/front/src/style/components/_button.scss b/front/src/style/components/_button.scss index d47d804bb3..925212d9a2 100644 --- a/front/src/style/components/_button.scss +++ b/front/src/style/components/_button.scss @@ -139,7 +139,7 @@ button.reset { .trackPosition { cursor: pointer; - display: inline-block; + display: contents; min-height: 1em; outline: none; border: none; diff --git a/front/src/style/components/_play_indicator.scss b/front/src/style/components/_play_indicator.scss new file mode 100644 index 0000000000..d56df945d6 --- /dev/null +++ b/front/src/style/components/_play_indicator.scss @@ -0,0 +1,29 @@ +#audio-bars { + height: 1em; + position: relative; +} + +.audio-bar { + background: var(--main-color); + bottom: 0; + height: .1em; + position: absolute; + width: 3px; + animation: sound 1s cubic-bezier(.17,.37,.43,.67) infinite alternate; +} + +@keyframes sound { + 0% { + opacity: .35; + height: .1em; + } + 100% { + opacity: 1; + height: 1em; + } +} + +.audio-bar:nth-child(1) { left: 0em; animation-duration: 0.4s; } +.audio-bar:nth-child(2) { left: .25em; animation-duration: 0.2s; } +.audio-bar:nth-child(3) { left: .50em; animation-duration: 1.0s; } +.audio-bar:nth-child(4) { left: .75em; animation-duration: 0.3s; } diff --git a/front/src/style/globals/_channels.scss b/front/src/style/globals/_channels.scss index 6b706781ed..f305a79d48 100644 --- a/front/src/style/globals/_channels.scss +++ b/front/src/style/globals/_channels.scss @@ -105,6 +105,9 @@ .ui.really.tiny.button.play-button.playing { color: var(--vibrant-color); visibility: visible; + display: contents; + left: auto; + right: auto; } .ui.really.tiny.button.play-button.paused { color: var(--vibrant-color); @@ -139,6 +142,9 @@ .ui.really.tiny.button.play-button { color: var(--main-color); visibility: visible; + display: contents; + left: auto; + right: auto; } .ui.floating.dropdown { visibility: visible; -- GitLab