Added user page. fixed player.
Build and Publish / Build and Publish Docker Image (push) Successful in 3m32s
Build and Publish / Build and Publish Docker Image (push) Successful in 3m32s
This commit is contained in:
@@ -46,6 +46,7 @@ const T = {
|
||||
radioFailed: "{{ t.player_radio_failed }}",
|
||||
connectionLost: "{{ t.player_connection_lost }}",
|
||||
connectionLostDetail: "{{ t.player_connection_lost_detail }}",
|
||||
activeDevice: "{{ t.player_active_device }}",
|
||||
trackWord: "{{ t.player_tracks_count }}",
|
||||
releaseWord: "{{ t.player_releases_count }}",
|
||||
ofWord: "{{ t.player_of }}",
|
||||
@@ -1176,6 +1177,10 @@ document.addEventListener('alpine:init', () => {
|
||||
jamUsers: [],
|
||||
jamSelectedUsers: [],
|
||||
jamSearching: false,
|
||||
remoteHintVisible: false,
|
||||
remoteHintDeviceName: '',
|
||||
_remoteHintShown: false,
|
||||
_remoteHintTimer: null,
|
||||
_pollTimer: null,
|
||||
_jamSearchTimer: null,
|
||||
_stateRefreshTick: 0,
|
||||
@@ -1265,6 +1270,34 @@ document.addEventListener('alpine:init', () => {
|
||||
if (wasActive && !this.isActive()) {
|
||||
Alpine.store('player')?._pauseLocal();
|
||||
}
|
||||
this._maybeShowRemoteHint();
|
||||
},
|
||||
|
||||
remoteActiveDevice() {
|
||||
if (!this.activeDeviceId || this.activeDeviceId === this.id) return null;
|
||||
return this.devices.find(device => device.id === this.activeDeviceId) || null;
|
||||
},
|
||||
|
||||
_maybeShowRemoteHint() {
|
||||
if (this._remoteHintShown || this.remoteHintVisible || this.open) return;
|
||||
const active = this.remoteActiveDevice();
|
||||
if (!active) return;
|
||||
this.remoteHintDeviceName = active.name || 'Devices';
|
||||
this.remoteHintVisible = true;
|
||||
this._remoteHintShown = true;
|
||||
clearTimeout(this._remoteHintTimer);
|
||||
this._remoteHintTimer = setTimeout(() => this.dismissRemoteHint(), 60000);
|
||||
},
|
||||
|
||||
dismissRemoteHint() {
|
||||
this.remoteHintVisible = false;
|
||||
this._remoteHintShown = true;
|
||||
clearTimeout(this._remoteHintTimer);
|
||||
this._remoteHintTimer = null;
|
||||
},
|
||||
|
||||
remoteHintText() {
|
||||
return `${T.activeDevice}: ${this.remoteHintDeviceName || this.activeLabel()}`;
|
||||
},
|
||||
|
||||
selectedJam() {
|
||||
@@ -1330,6 +1363,7 @@ document.addEventListener('alpine:init', () => {
|
||||
},
|
||||
|
||||
toggle() {
|
||||
this.dismissRemoteHint();
|
||||
this.open = !this.open;
|
||||
if (this.open) this.poll();
|
||||
},
|
||||
|
||||
+36
-12
@@ -639,23 +639,37 @@
|
||||
<span style="text-align:right">{{ t.player_duration }}</span>
|
||||
</div>
|
||||
<template x-for="(track, idx) in $store.library.currentArtist.featured_tracks" :key="track.id">
|
||||
<div class="track-row"
|
||||
<div class="track-row artist-appearance-row"
|
||||
:class="{ playing: $store.player.currentTrack && $store.player.currentTrack.id === track.id }"
|
||||
@dblclick="$store.queue.playRelease($store.library.currentArtist.featured_tracks, idx)">
|
||||
<span class="track-num" x-text="idx + 1"></span>
|
||||
<div class="track-info">
|
||||
<div class="track-title">
|
||||
<span x-text="track.title"></span>
|
||||
<span style="color:var(--text-subdued)"> · </span>
|
||||
<a class="artist-link" @click.stop="$store.library.openRelease(track.release_id)" x-text="track.release_title"></a>
|
||||
</div>
|
||||
<div class="track-artists-inline">
|
||||
<template x-for="(artist, artistIdx) in $store.library.trackArtistLinks(track)" :key="artist.label + '-' + artist.id + '-' + artistIdx">
|
||||
<span>
|
||||
<template x-if="artistIdx > 0"><span>, </span></template>
|
||||
<a class="artist-link" @click.stop="$store.library.openArtist(artist.id)" x-text="artist.label"></a>
|
||||
</span>
|
||||
<button class="artist-appearance-cover"
|
||||
type="button"
|
||||
@click.stop="$store.library.openRelease(track.release_id)"
|
||||
:title="track.release_title"
|
||||
aria-label="{{ t.player_release }}">
|
||||
<template x-if="track.cover_url">
|
||||
<img :src="track.cover_url" :alt="track.release_title" loading="lazy">
|
||||
</template>
|
||||
<template x-if="!track.cover_url">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="12" cy="12" r="4"/></svg>
|
||||
</template>
|
||||
</button>
|
||||
<div class="artist-appearance-copy">
|
||||
<div class="track-title">
|
||||
<span x-text="track.title"></span>
|
||||
<span style="color:var(--text-subdued)"> · </span>
|
||||
<a class="artist-link" @click.stop="$store.library.openRelease(track.release_id)" x-text="track.release_title"></a>
|
||||
</div>
|
||||
<div class="track-artists-inline">
|
||||
<template x-for="(artist, artistIdx) in $store.library.trackArtistLinks(track)" :key="artist.label + '-' + artist.id + '-' + artistIdx">
|
||||
<span>
|
||||
<template x-if="artistIdx > 0"><span>, </span></template>
|
||||
<a class="artist-link" @click.stop="$store.library.openArtist(artist.id)" x-text="artist.label"></a>
|
||||
</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span></span>
|
||||
@@ -1074,6 +1088,16 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
||||
</button>
|
||||
<div class="device-picker" @click.outside="$store.devices.open = false">
|
||||
<template x-if="$store.devices.remoteHintVisible">
|
||||
<button class="remote-playback-hint"
|
||||
type="button"
|
||||
@click="$store.devices.dismissRemoteHint()"
|
||||
x-transition
|
||||
x-cloak>
|
||||
<span class="remote-playback-hint-text" x-text="$store.devices.remoteHintText()"></span>
|
||||
<span class="remote-playback-hint-progress"></span>
|
||||
</button>
|
||||
</template>
|
||||
<button class="queue-toggle-btn device-toggle-btn"
|
||||
:class="{ active: $store.devices.isActive() }"
|
||||
@click="$store.devices.toggle()"
|
||||
|
||||
+126
-10
@@ -735,6 +735,47 @@ button.user-stat:hover {
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.artist-appearance-row .track-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.artist-appearance-cover {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background: var(--bg-elevated);
|
||||
color: var(--text-subdued);
|
||||
cursor: pointer;
|
||||
flex: 0 0 42px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.artist-appearance-cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.artist-appearance-cover svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.artist-appearance-cover:hover {
|
||||
filter: brightness(1.14);
|
||||
}
|
||||
|
||||
.artist-appearance-copy {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.track-album { font-size: 13px; color: var(--text-subdued); }
|
||||
.track-duration { font-size: 13px; color: var(--text-subdued); text-align: right; pointer-events: none; }
|
||||
|
||||
@@ -1370,19 +1411,35 @@ button.user-stat:hover {
|
||||
|
||||
.volume-slider {
|
||||
width: 104px;
|
||||
height: 4px;
|
||||
background: var(--bg-active);
|
||||
border-radius: 2px;
|
||||
height: 28px;
|
||||
background: transparent;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.volume-slider::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--bg-active);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.volume-slider-fill {
|
||||
height: 100%;
|
||||
height: 4px;
|
||||
background: var(--text-primary);
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.volume-slider:hover .volume-slider-fill { background: var(--accent); }
|
||||
@@ -1452,6 +1509,51 @@ button.user-stat:hover {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.remote-playback-hint {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 38px;
|
||||
width: max-content;
|
||||
max-width: min(280px, calc(100vw - 24px));
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-elevated);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 14px 34px rgba(0,0,0,0.46);
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
overflow: hidden;
|
||||
padding: 10px 12px 12px;
|
||||
text-align: left;
|
||||
z-index: 44;
|
||||
}
|
||||
|
||||
.remote-playback-hint-text {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.remote-playback-hint-progress {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: var(--accent);
|
||||
transform-origin: left center;
|
||||
animation: remote-playback-hint-progress 60s linear forwards;
|
||||
}
|
||||
|
||||
@keyframes remote-playback-hint-progress {
|
||||
from { transform: scaleX(1); }
|
||||
to { transform: scaleX(0); }
|
||||
}
|
||||
|
||||
.device-popover {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
@@ -4164,13 +4266,19 @@ button.user-stat:hover {
|
||||
}
|
||||
|
||||
.volume-slider {
|
||||
display: block;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 9px;
|
||||
border-radius: 999px;
|
||||
height: 34px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.volume-slider-fill {
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.volume-slider::before {
|
||||
height: 7px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
@@ -4417,8 +4525,8 @@ button.user-stat:hover {
|
||||
}
|
||||
|
||||
.player-bar.mobile-expanded .mobile-full-player .volume-slider {
|
||||
display: block;
|
||||
height: 7px;
|
||||
display: flex;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.player-bar.mobile-expanded .mobile-full-player .device-popover {
|
||||
@@ -4432,6 +4540,14 @@ button.user-stat:hover {
|
||||
max-height: 42dvh;
|
||||
}
|
||||
|
||||
.player-bar.mobile-expanded .mobile-full-player .remote-playback-hint {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
bottom: calc(90px + var(--safe-bottom));
|
||||
right: 18px;
|
||||
max-width: calc(100vw - 36px);
|
||||
}
|
||||
|
||||
.player-bar.mobile-expanded .mobile-full-player .mobile-expanded-queue {
|
||||
display: block;
|
||||
width: 100%;
|
||||
@@ -5053,7 +5169,7 @@ button.user-stat:hover {
|
||||
}
|
||||
|
||||
.volume-slider {
|
||||
display: block;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user