Fix #1796, Potentially fix #1471

This commit is contained in:
wvffle 2022-07-22 03:02:53 +00:00 committed by Georg Krause
parent 9234720710
commit 53d9015e17
4 changed files with 409 additions and 378 deletions

View File

@ -97,14 +97,14 @@ if (store.state.auth.authenticated) {
<set-instance-modal v-model:show="showSetInstanceModal" />
<service-messages />
<transition name="queue">
<queue v-if="store.state.ui.queueFocused" />
<queue v-show="store.state.ui.queueFocused" />
</transition>
<router-view v-slot="{ Component }">
<template v-if="Component">
<keep-alive :max="1">
<Suspense v-if="!store.state.ui.queueFocused">
<component :is="Component" />
<Suspense>
<component :is="Component" v-show="!store.state.ui.queueFocused" />
<template #fallback>
<!-- TODO (wvffle): Add loader -->
Loading...

View File

@ -1,13 +1,13 @@
<script setup lang="ts">
import { useStore } from '~/store'
import { nextTick, onMounted, ref, computed, onBeforeMount, onUnmounted } from 'vue'
import { nextTick, ref, computed, onBeforeMount, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import time from '~/utils/time'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import Draggable from 'vuedraggable'
import { whenever, useTimeoutFn, useWindowScroll, useWindowSize } from '@vueuse/core'
import { whenever, watchDebounced } from '@vueuse/core'
import { useGettext } from 'vue3-gettext'
import useQueue from '~/composables/audio/useQueue'
import usePlayer from '~/composables/audio/usePlayer'
@ -28,33 +28,18 @@ activate()
const store = useStore()
const currentIndex = computed(() => store.state.queue.currentIndex)
const { y: pageYOffset } = useWindowScroll()
const { height: windowHeight } = useWindowSize()
const scrollToCurrent = async () => {
await nextTick()
const item = queueModal.value?.querySelector('.queue-item.active')
const { top } = item?.getBoundingClientRect() ?? { top: 0 }
window.scrollTo({
top: top + pageYOffset.value - windowHeight.value / 2,
behavior: 'smooth'
})
item?.scrollIntoView({ behavior: store.state.ui.queueFocused ? 'smooth' : 'auto' })
}
onMounted(async () => {
await nextTick()
// NOTE: delay is to let transition work
useTimeoutFn(scrollToCurrent, 400)
})
const { $pgettext } = useGettext()
const {
playing,
loading: isLoadingAudio,
errored,
focused: playerFocused,
duration,
durationFormatted,
currentTimeFormatted,
@ -66,7 +51,6 @@ const {
} = usePlayer()
const {
focused: queueFocused,
currentTrack,
hasNext,
isEmpty: emptyQueue,
@ -94,7 +78,7 @@ const labels = computed(() => ({
selectTrack: $pgettext('*/*/*', 'Select track')
}))
whenever(queueFocused, scrollToCurrent, { immediate: true })
watchDebounced(() => store.state.ui.queueFocused, scrollToCurrent, { debounce: 400 })
whenever(currentTrack, scrollToCurrent, { immediate: true })
whenever(
@ -124,18 +108,14 @@ const play = (index: number) => {
class="main with-background component-queue"
:aria-label="labels.queue"
>
<div :class="['ui vertical stripe queue segment', playerFocused ? 'player-focused' : '']">
<div class="ui fluid container">
<div
id="queue-grid"
class="ui stackable grid"
>
<div class="ui six wide column current-track">
<div id="queue-grid">
<div
id="player"
class="ui basic segment"
>
<template v-if="currentTrack">
<div class="cover-container">
<div class="cover">
<img
v-if="currentTrack.cover && currentTrack.cover.urls.large_square_crop"
ref="cover"
@ -154,6 +134,8 @@ const play = (index: number) => {
alt=""
src="../assets/audio/default-cover.png"
>
</div>
</div>
<h1 class="ui header">
<div class="content ellipsis">
<router-link
@ -202,7 +184,7 @@ const play = (index: number) => {
</translate>
</p>
</div>
<div class="additional-controls tablet-and-below">
<div class="additional-controls desktop-and-below">
<track-favorite-icon
v-if="$store.state.auth.authenticated"
:track="currentTrack"
@ -269,7 +251,7 @@ const play = (index: number) => {
</template>
</div>
</div>
<div class="player-controls tablet-and-below">
<div class="player-controls desktop-and-below">
<span
role="button"
:title="labels.previous"
@ -314,9 +296,8 @@ const play = (index: number) => {
</div>
</template>
</div>
</div>
<div class="ui ten wide column queue-column">
<div class="ui basic clearing fixed-header segment">
<div>
<div class="ui basic clearing segment">
<h2 class="ui header">
<div class="content">
<button
@ -355,12 +336,13 @@ const play = (index: number) => {
</div>
</h2>
</div>
<div>
<table class="ui compact very basic fixed single line selectable unstackable table">
<draggable
v-model="tracks"
tag="tbody"
handle=".handle"
item-key="id"
tag="tbody"
@update="reorder"
>
<template #item="{ element: track, index }">
@ -431,7 +413,6 @@ const play = (index: number) => {
</template>
</draggable>
</table>
<div
v-if="$store.state.radios.running"
class="ui info message"
@ -460,6 +441,5 @@ const play = (index: number) => {
</div>
</div>
</div>
</div>
</section>
</template>

View File

@ -1,28 +1,26 @@
.queue.segment.player-focused #queue-grid #player {
@include media("<desktop") {
padding-bottom: $bottom-player-height + 2rem;
}
}
.queue-controls {
@include media("<desktop") {
height: $bottom-player-height;
}
}
.ui.fixed-header.segment {
.ui.clearing.segment {
background-color: var(--site-background);
box-shadow: var(--secondary-menu-box-shadow);
margin: 0 !important;
}
.queue-enter-active, .queue-leave-active {
transition: all 0.2s ease-in-out;
.current-track, .queue-column {
opacity: 0;
}
.queue-enter-active,
.queue-leave-active {
transition: all 0.2s ease;
will-change: transform, opacity;
}
.queue-enter, .queue-leave-to {
transform: translateY(100vh);
.queue-enter-from,
.queue-leave-to {
opacity: 0;
transform: translateY(5vh);
}
.component-queue {
@ -33,11 +31,10 @@
}
}
&.main {
position: absolute;
position: fixed;
min-height: 100vh;
width: 100vw;
z-index: 1000;
padding-bottom: 3em;
}
&.main > .button {
position: fixed;
@ -48,25 +45,13 @@
display: none;
}
}
.queue.segment:not(.player-focused) {
#player {
@include media("<desktop") {
height: 0;
display: none;
}
}
}
.queue.segment #player {
padding: 0em;
> * {
padding: 0.5em;
}
}
.player-focused .grid > .ui.queue-column {
@include media("<desktop") {
display: none;
}
}
.queue-column {
overflow-y: auto;
}
@ -116,7 +101,7 @@
@include media("<desktop") {
padding: 1em;
}
@include media(">desktop") {
@include media(">=desktop") {
right: 1em;
left: 38%;
}
@ -168,8 +153,7 @@
overflow: hidden;
}
.progress-wrapper, .warning.message {
max-width: 25em;
margin: 0 auto;
width: 25em;
}
.ui.progress .bar {
transition: none;
@ -243,3 +227,65 @@
}
}
// Wvffle's styles
.component-queue {
#queue-grid {
display: grid;
grid-template-columns: 37.5% 62.5%;
@include media("<desktop") {
grid-template-columns: 1fr 0;
}
#player {
display: flex;
flex-flow: column;
align-items: center;
justify-content: center;
text-align: center;
.cover-container {
width: 50vh;
max-width: 90%;
.cover {
height: 0;
width: 100%;
padding-bottom: 100%;
position: relative;
img {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
}
}
}
.progress-wrapper {
font-size: 1.5rem;
width: 54vh;
max-width: 90%;
}
}
> div {
height: calc(100vh - 4rem);
margin: 0 !important;
&:nth-child(2) {
display: grid;
grid-template-rows: auto 1fr;
> :nth-child(2) {
overflow-y: auto;
padding-bottom: 2rem;
}
}
}
}
}

View File

@ -53,6 +53,11 @@ a {
display: none !important;
}
}
.desktop-and-below {
@include media(">=desktop") {
display: none !important;
}
}
.tablet-and-up {
@include media("<tablet") {
display: none !important;