Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions components/app/Appbar.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<template>
<div class="w-full h-16 bg-primary relative z-20">
<div id="appbar" class="absolute top-0 left-0 w-full h-full flex items-center px-2">
<nuxt-link v-show="!showBack" to="/" class="mr-3">
<nuxt-link v-show="!showBack" to="/" class="mr-3" :aria-label="$strings.ButtonHome">
<img src="/Logo.png" class="h-10 w-10" />
</nuxt-link>
<a v-if="showBack" @click="back" aria-label="Back" class="rounded-full h-10 w-10 flex items-center justify-center mr-2 cursor-pointer">
<a v-if="showBack" @click="back" :aria-label="$strings.ButtonBack" class="rounded-full h-10 w-10 flex items-center justify-center mr-2 cursor-pointer">
<span class="material-symbols text-3xl text-fg">arrow_back</span>
</a>
<div v-if="user && currentLibrary">
<button type="button" aria-label="Show library modal" class="pl-1.5 pr-2.5 py-2 bg-bg bg-opacity-30 rounded-md flex items-center" @click="clickShowLibraryModal">
<ui-library-icon :icon="currentLibraryIcon" :size="4" font-size="base" />
<!-- Removed hardcoded string. You want the accessibile name of the control to match or contain the visible label -->
<button type="button" aria-haspopup="dialog" class="pl-1.5 pr-2.5 py-2 bg-bg bg-opacity-30 rounded-md flex items-center" @click="clickShowLibraryModal">
<ui-library-icon :icon="currentLibraryIcon" :aria-label="$strings.ButtonLibrary" role="img" :size="4" font-size="base" />
<p class="text-sm leading-4 ml-2 mt-0.5 max-w-24 truncate">{{ currentLibraryName }}</p>
</button>
</div>
Expand All @@ -21,19 +22,28 @@
<widgets-download-progress-indicator />

<!-- Must be connected to a server to cast, only supports media items on server -->
<button type="button" aria-label="Cast" v-show="isCastAvailable && user" class="mx-2 cursor-pointer flex items-center" @click="castClick">
<!-- There are two states here for casting so we want the aria-label to reflect that -->
<button type="button" :aria-label="isCasting ? $strings.ButtonCastConnected : $strings.ButtonCast" v-show="isCastAvailable && user" class="mx-2 cursor-pointer flex items-center" @click="castClick">
<span class="material-symbols text-2xl leading-none">
{{ isCasting ? 'cast_connected' : 'cast' }}
</span>
</button>

<nuxt-link v-if="user" class="mx-1.5 flex items-center h-10" :aria-label="$strings.ButtonSearch" to="/search">
<span class="material-symbols text-2xl leading-none">search</span>
</nuxt-link>

<div class="h-7 mx-1.5">
<span class="material-symbols" role="button" aria-haspopup="dialog" :aria-label="$strings.ButtonNavDrawer" tabindex="0" @keyup.enter="clickShowSideDrawer" style="font-size: 1.75rem" @click="clickShowSideDrawer">menu</span>
</div>
<nuxt-link v-if="user" class="mx-1.5 flex items-center h-10" to="/search" aria-label="Search">
<span class="material-symbols text-2xl leading-none">search</span>
</nuxt-link>

<button type="button" aria-label="Toggle side drawer" class="h-7 mx-1.5" @click="clickShowSideDrawer">
<button type="button" aria-haspopup="dialog" :aria-label="$strings.ButtonNavDrawer" class="h-7 mx-1.5" @click="clickShowSideDrawer">
<span class="material-symbols" style="font-size: 1.75rem">menu</span>
</button>

</div>
</div>
</template>
Expand Down
103 changes: 85 additions & 18 deletions components/app/AudioPlayer.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<template>
<div v-if="playbackSession" id="streamContainer" class="fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="{ fullscreen: showFullscreen, 'ios-player': $platform === 'ios', 'web-player': $platform === 'web' }">
<div v-if="playbackSession" :aria-label="$strings.LabelAudioPlayerRegion" role="region" id="streamContainer" class="fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="{ fullscreen: showFullscreen, 'ios-player': $platform === 'ios', 'web-player': $platform === 'web' }">
<div v-if="showFullscreen" class="w-full h-full z-10 absolute top-0 left-0 pointer-events-auto" :style="{ backgroundColor: coverRgb }">
<div class="w-full h-full absolute top-0 left-0 pointer-events-none" style="background: var(--gradient-audio-player)" />

<div class="top-4 left-4 absolute cursor-pointer">
<span class="material-symbols text-5xl" :class="{ 'text-black text-opacity-75': coverBgIsLight && theme !== 'black' }" @click="collapseFullscreen">keyboard_arrow_down</span>
<span role="button" tabindex="0" ref="collapseFullscreenButton" :aria-label="$strings.LabelClosePlayer" class="material-symbols text-5xl" :class="{ 'text-black text-opacity-75': coverBgIsLight && theme !== 'black' }" @keyup.enter="collapseFullscreen" @click="collapseFullscreen">keyboard_arrow_down</span>
</div>
<div v-show="showCastBtn" class="top-6 right-16 absolute cursor-pointer">
<span class="material-symbols text-3xl" :class="coverBgIsLight && theme !== 'black' ? 'text-black' : ''" @click="castClick">{{ isCasting ? 'cast_connected' : 'cast' }}</span>
<span role="button" :aria-label="isCasting ? $strings.ButtonCastConnected : $strings.ButtonCast" @keyup.enter="castClick" class="material-symbols text-3xl" :class="coverBgIsLight && theme !== 'black' ? 'text-black' : ''" @click="castClick">{{ isCasting ? 'cast_connected' : 'cast' }}</span>
</div>
<div class="top-6 right-4 absolute cursor-pointer">
<span class="material-symbols text-3xl" :class="{ 'text-black text-opacity-75': coverBgIsLight && theme !== 'black' }" @click="showMoreMenuDialog = true">more_vert</span>
Expand All @@ -30,8 +30,8 @@
</div>
</div>

<div class="cover-wrapper absolute z-30 pointer-events-auto" @click="clickContainer">
<div class="w-full h-full flex justify-center">
<div :role="!showFullscreen ? 'button' : null" :tabindex="!showFullscreen ? 0 : null" ref="expandFullScreenButton" :aria-label="showFullscreen ? null : $strings.LabelOpenFullscreenPlayer" class="cover-wrapper absolute z-30 pointer-events-auto" @keyup.enter="clickContainer" @click="clickContainer">
<div aria-hidden="true" class="w-full h-full flex justify-center">
<covers-book-cover v-if="libraryItem || localLibraryItemCoverSrc" ref="cover" :library-item="libraryItem" :download-cover="localLibraryItemCoverSrc" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" raw @imageLoaded="coverImageLoaded" />
</div>

Expand All @@ -40,7 +40,7 @@
</div>
</div>

<div class="title-author-texts absolute z-30 left-0 right-0 overflow-hidden" @click="clickTitleAndAuthor">
<div :role="showFullscreen ? 'button' : 'none'" :tabindex="showFullscreen ? 0 : -1" class="title-author-texts absolute z-30 left-0 right-0 overflow-hidden" @keyup.enter="clickTitleAndAuthor" @click="clickTitleAndAuthor">
<div ref="titlewrapper" class="overflow-hidden relative">
<p class="title-text whitespace-nowrap"></p>
</div>
Expand All @@ -50,39 +50,92 @@
<div id="playerContent" class="playerContainer w-full z-20 absolute bottom-0 left-0 right-0 p-2 pointer-events-auto transition-all" :style="{ backgroundColor: showFullscreen ? '' : coverRgb }" @click="clickContainer">
<div v-if="showFullscreen" class="absolute bottom-4 left-0 right-0 w-full pb-4 pt-2 mx-auto px-6" style="max-width: 414px">
<div class="flex items-center justify-between pointer-events-auto">
<span v-if="!isPodcast && serverLibraryItemId && socketConnected" class="material-symbols text-3xl text-fg-muted cursor-pointer" :class="{ fill: bookmarks.length }" @click="$emit('showBookmarks')">bookmark</span>
<span v-if="!isPodcast && serverLibraryItemId && socketConnected" role="button" tabindex="0" aria-haspopup="dialog" :aria-label="bookmarks.length ? $strings.LabelYourBookmarks : $strings.ButtonCreateBookmark" class="material-symbols text-3xl text-fg-muted cursor-pointer" :class="{ fill: bookmarks.length }" @keyup.enter="$emit('showBookmarks')" @click="$emit('showBookmarks')"
>bookmark</span
>
<!-- hidden for podcasts but still using this as a placeholder -->
<span v-else class="material-symbols text-3xl text-white text-opacity-0">bookmark</span>
<span v-else aria-hidden="true" class="material-symbols text-3xl text-white text-opacity-0">bookmark</span>

<span class="font-mono text-fg-muted cursor-pointer" style="font-size: 1.35rem" @click="$emit('selectPlaybackSpeed')">{{ currentPlaybackRate }}x</span>
<svg v-if="!sleepTimerRunning" xmlns="http://www.w3.org/2000/svg" class="h-7 w-7 text-fg-muted cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" @click.stop="$emit('showSleepTimer')">
<span role="button" tabindex="0" aria-haspopup="dialog" :aria-label="`${$strings.LabelPlaybackSpeed}: ${currentPlaybackRate}x`" class="font-mono text-fg-muted cursor-pointer" style="font-size: 1.35rem" @keyup.enter="$emit('selectPlaybackSpeed')" @click="$emit('selectPlaybackSpeed')">{{ currentPlaybackRate }}x</span>
<svg v-if="!sleepTimerRunning" xmlns="http://www.w3.org/2000/svg" role="button" :aria-label="$strings.LabelSleepTimer" class="h-7 w-7 text-fg-muted cursor-pointer" fill="none" viewBox="0 0 24 24" stroke="currentColor" @keyup.enter.stop="$emit('showSleepTimer')" @click.stop="$emit('showSleepTimer')">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
</svg>
<div v-else class="h-7 w-7 flex items-center justify-around cursor-pointer" @click.stop="$emit('showSleepTimer')">
<div v-else role="button" tabindex="0" aria-haspopup="dialog" :aria-label="`${$strings.LabelSleepTimer} - ${sleepTimeRemainingPretty}`" class="h-7 w-7 flex items-center justify-around cursor-pointer" @keyup.enter.stop="$emit('showSleepTimer')" @click.stop="$emit('showSleepTimer')">
<p class="text-xl font-mono text-success">{{ sleepTimeRemainingPretty }}</p>
</div>

<span class="material-symbols text-3xl text-fg cursor-pointer" :class="chapters.length ? 'text-opacity-75' : 'text-opacity-10'" @click="clickChaptersBtn">format_list_bulleted</span>
<span class="material-symbols text-3xl text-fg cursor-pointer" role="button" :aria-label="$strings.LabelChapters" aria-haspopup="dialog" :class="chapters.length ? 'text-opacity-75' : 'text-opacity-10'" @keyup.enter="clickChaptersBtn" @click="clickChaptersBtn">format_list_bulleted</span>
</div>
</div>
<div v-else class="w-full h-full absolute top-0 left-0 pointer-events-none" style="background: var(--gradient-minimized-audio-player)" />

<div id="playerControls" class="absolute right-0 bottom-0 mx-auto" style="max-width: 414px">
<div class="flex items-center max-w-full" :class="playerSettings.lockUi ? 'justify-center' : 'justify-between'">
<span v-show="showFullscreen && !playerSettings.lockUi" class="material-symbols next-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpChapterStart">first_page</span>
<span v-show="!playerSettings.lockUi" class="material-symbols jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpBackwards">{{ jumpBackwardsIcon }}</span>
<div class="play-btn cursor-pointer shadow-sm flex items-center justify-center rounded-full text-primary mx-4 relative overflow-hidden" :style="{ backgroundColor: coverRgb }" :class="{ 'animate-spin': seekLoading }" @mousedown.prevent @mouseup.prevent @click.stop="playPauseClick">
<span
role="button"
:aria-hidden="isLoading && !showFullscreen"
:tabindex="isLoading && !showFullscreen ? -1 : 0"
:aria-label="$strings.ButtonPreviousChapter"
v-show="showFullscreen && !playerSettings.lockUi"
:aria-disabled="isLoading"
class="material-symbols next-icon text-fg cursor-pointer"
:class="isLoading ? 'text-opacity-10' : 'text-opacity-75'"
@keyup.enter.stop="jumpChapterStart"
@click.stop="jumpChapterStart"
>first_page</span
>
<span role="button" :tabindex="isLoading ? -1 : 0" :aria-label="$getString('ButtonJumpBackwards', [jumpBackwardsTime])" v-show="!playerSettings.lockUi" :aria-disabled="isLoading" class="material-symbols jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @keyup.enter.stop="jumpBackwards" @click.stop="jumpBackwards">{{ jumpBackwardsIcon }}</span>
<div
role="button"
:tabindex="seekLoading ? -1 : 0"
:aria-disabled="seekLoading"
@keyup.enter.stop="playPauseClick"
:aria-label="seekLoading ? $strings.MessageLoading : !isPlaying ? $strings.ButtonPlay : $strings.ButtonPause"
class="play-btn cursor-pointer shadow-sm flex items-center justify-center rounded-full text-primary mx-4 relative overflow-hidden"
:style="{ backgroundColor: coverRgb }"
:class="{ 'animate-spin': seekLoading }"
@mousedown.prevent
@mouseup.prevent
@click.stop="playPauseClick"
>
<div v-if="!coverBgIsLight" class="absolute top-0 left-0 w-full h-full bg-white bg-opacity-20 pointer-events-none" />

<span v-if="!isLoading" class="material-symbols fill" :class="{ 'text-white': coverRgb && !coverBgIsLight }">{{ seekLoading ? 'autorenew' : !isPlaying ? 'play_arrow' : 'pause' }}</span>
<widgets-spinner-icon v-else class="h-8 w-8" />
</div>
<span v-show="!playerSettings.lockUi" class="material-symbols jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpForward">{{ jumpForwardIcon }}</span>
<span v-show="showFullscreen && !playerSettings.lockUi" class="material-symbols next-icon text-fg cursor-pointer" :class="nextChapter && !isLoading ? 'text-opacity-75' : 'text-opacity-10'" @click.stop="jumpNextChapter">last_page</span>
<span role="button" :tabindex="isLoading ? -1 : 0" :aria-label="$getString('ButtonJumpForward', [jumpForwardTime])" v-show="!playerSettings.lockUi" :aria-disabled="isLoading" class="material-symbols jump-icon text-fg cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @keyup.enter.stop="jumpForward" @click.stop="jumpForward">{{ jumpForwardIcon }}</span>
<span
role="button"
:aria-hidden="isLoading && !showFullscreen"
:tabindex="isLoading && !showFullscreen ? -1 : 0"
:aria-label="$strings.ButtonNextChapter"
v-show="showFullscreen && !playerSettings.lockUi"
:aria-disabled="isLoading"
class="material-symbols next-icon text-fg cursor-pointer"
:class="nextChapter && !isLoading ? 'text-opacity-75' : 'text-opacity-10'"
@keyup.enter.stop="jumpNextChapter"
@click.stop="jumpNextChapter"
>last_page</span
>
</div>
</div>

<div id="playerTrack" class="absolute left-0 w-full px-6">
<div
id="playerTrack"
class="absolute left-0 w-full px-6"
:tabindex="isLoading ? -1 : 0"
:aria-disabled="isLoading"
:aria-valuetext="`${$strings.LabelProgress}: ${$elapsedPretty(currentTime)} / ${$getString('LabelTimeRemaining', [$elapsedPretty(timeRemaining)])}`"
aria-valuemin="0"
:aria-valuenow="currentTime.toFixed(0)"
:aria-valuemax="totalDuration.toFixed(0)"
role="slider"
:aria-label="$strings.LabelProgress"
@keyup.down.stop="jumpBackwards"
@keyup.left.stop="jumpBackwards"
@keyup.up.stop="jumpForward"
@keyup.right.stop="jumpForward"
>
<div class="flex pointer-events-none">
<p class="font-mono text-fg" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
<div class="flex-grow" />
Expand Down Expand Up @@ -444,12 +497,26 @@ export default {
this.$nextTick(() => {
this.updateTrack()
})
this.focusOnCollapseFullScreenButton()
},
collapseFullscreen() {
this.showFullscreen = false
if (this.titleMarquee) this.titleMarquee.reset()

this.forceCloseDropdownMenu()
this.focusOnExpandFullscreenButton()
},
focusOnCollapseFullScreenButton() {
this.$nextTick(() => {
const collapseFullscreenButton = this.$refs.collapseFullscreenButton
collapseFullscreenButton.focus()
})
},
focusOnExpandFullscreenButton() {
this.$nextTick(() => {
const expandFullScreenButton = this.$refs.expandFullScreenButton
expandFullScreenButton.focus()
})
},
async jumpNextChapter() {
await this.$hapticsImpact()
Expand Down
4 changes: 2 additions & 2 deletions components/bookshelf/LazyBookshelf.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div id="bookshelf" class="w-full max-w-full h-full">
<div role="list" id="bookshelf" class="w-full max-w-full h-full">
<template v-for="shelf in totalShelves">
<div :key="shelf" class="w-full px-2 relative" :class="showBookshelfListView || altViewEnabled ? '' : 'bookshelfRow'" :id="`shelf-${shelf - 1}`" :style="{ height: shelfHeight + 'px' }">
<div :key="shelf" class="w-full px-2 relative" :class="showBookshelfListView || altViewEnabled ? '' : 'bookshelfRow'" :id="`shelf-${shelf - 1}`" role="none" :style="{ height: shelfHeight + 'px' }">
<div v-if="!showBookshelfListView && !altViewEnabled" class="w-full absolute bottom-0 left-0 z-30 bookshelfDivider" style="min-height: 16px" :class="`h-${shelfDividerHeightIndex}`" />
<div v-else-if="showBookshelfListView" class="flex border-t border-white border-opacity-10" />
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/bookshelf/Shelf.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<template>
<div class="w-full relative">
<div v-if="altViewEnabled" class="px-5 pb-3 pt-4">
<p class="font-semibold" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ label }}</p>
<p role="heading" aria-level="2" class="font-semibold" :style="{ fontSize: sizeMultiplier + 'rem' }">{{ label }}</p>
</div>

<div class="flex items-end px-3 max-w-full overflow-x-auto" :class="altViewEnabled ? '' : 'bookshelfRow'" :style="{ height: shelfHeight + 'px', paddingBottom: entityPaddingBottom + 'px' }">
<div role="list" :aria-label="label" class="flex items-end px-3 max-w-full overflow-x-auto" :class="altViewEnabled ? '' : 'bookshelfRow'" :style="{ height: shelfHeight + 'px', paddingBottom: entityPaddingBottom + 'px' }">
<template v-for="(entity, index) in entities">
<cards-lazy-book-card v-if="type === 'book' || type === 'podcast'" :key="entity.id" :index="index" :book-mount="entity" :width="bookWidth" :height="entityHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :is-alt-view-enabled="altViewEnabled" class="mx-2 relative" />
<cards-lazy-book-card v-if="type === 'episode'" :key="entity.recentEpisode.id" :index="index" :book-mount="entity" :width="bookWidth" :height="entityHeight" :book-cover-aspect-ratio="bookCoverAspectRatio" :is-alt-view-enabled="altViewEnabled" class="mx-2 relative" />
Expand Down
Loading