small ui changes
This commit is contained in:
@@ -1 +1 @@
|
|||||||
cf76bbfd46ad4447fbfbaa4a1c6845ca
|
db9e9335c200a37f58ae820ffcfee304
|
||||||
Generated
-21
@@ -17,9 +17,6 @@ importers:
|
|||||||
'@lit-labs/virtualizer':
|
'@lit-labs/virtualizer':
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
htmx.org:
|
|
||||||
specifier: 2.0.8
|
|
||||||
version: 2.0.8
|
|
||||||
lit:
|
lit:
|
||||||
specifier: ^3.2.1
|
specifier: ^3.2.1
|
||||||
version: 3.3.2
|
version: 3.3.2
|
||||||
@@ -350,79 +347,66 @@ packages:
|
|||||||
resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
|
resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm-musleabihf@4.57.1':
|
'@rollup/rollup-linux-arm-musleabihf@4.57.1':
|
||||||
resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
|
resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-gnu@4.57.1':
|
'@rollup/rollup-linux-arm64-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
|
resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-arm64-musl@4.57.1':
|
'@rollup/rollup-linux-arm64-musl@4.57.1':
|
||||||
resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
|
resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-gnu@4.57.1':
|
'@rollup/rollup-linux-loong64-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
|
resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-loong64-musl@4.57.1':
|
'@rollup/rollup-linux-loong64-musl@4.57.1':
|
||||||
resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
|
resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==}
|
||||||
cpu: [loong64]
|
cpu: [loong64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-gnu@4.57.1':
|
'@rollup/rollup-linux-ppc64-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
|
resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-ppc64-musl@4.57.1':
|
'@rollup/rollup-linux-ppc64-musl@4.57.1':
|
||||||
resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
|
resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==}
|
||||||
cpu: [ppc64]
|
cpu: [ppc64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-gnu@4.57.1':
|
'@rollup/rollup-linux-riscv64-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
|
resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-riscv64-musl@4.57.1':
|
'@rollup/rollup-linux-riscv64-musl@4.57.1':
|
||||||
resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
|
resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-s390x-gnu@4.57.1':
|
'@rollup/rollup-linux-s390x-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
|
resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==}
|
||||||
cpu: [s390x]
|
cpu: [s390x]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-gnu@4.57.1':
|
'@rollup/rollup-linux-x64-gnu@4.57.1':
|
||||||
resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
|
resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [glibc]
|
|
||||||
|
|
||||||
'@rollup/rollup-linux-x64-musl@4.57.1':
|
'@rollup/rollup-linux-x64-musl@4.57.1':
|
||||||
resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
|
resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
libc: [musl]
|
|
||||||
|
|
||||||
'@rollup/rollup-openbsd-x64@4.57.1':
|
'@rollup/rollup-openbsd-x64@4.57.1':
|
||||||
resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
|
resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==}
|
||||||
@@ -709,9 +693,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==}
|
resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==}
|
||||||
engines: {node: '>=20.10'}
|
engines: {node: '>=20.10'}
|
||||||
|
|
||||||
htmx.org@2.0.8:
|
|
||||||
resolution: {integrity: sha512-fm297iru0iWsNJlBrjvtN7V9zjaxd+69Oqjh4F/Vq9Wwi2kFisLcrLCiv5oBX0KLfOX/zG8AUo9ROMU5XUB44Q==}
|
|
||||||
|
|
||||||
ignore@7.0.5:
|
ignore@7.0.5:
|
||||||
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
@@ -1685,8 +1666,6 @@ snapshots:
|
|||||||
|
|
||||||
html-tags@5.1.0: {}
|
html-tags@5.1.0: {}
|
||||||
|
|
||||||
htmx.org@2.0.8: {}
|
|
||||||
|
|
||||||
ignore@7.0.5: {}
|
ignore@7.0.5: {}
|
||||||
|
|
||||||
import-fresh@3.3.1:
|
import-fresh@3.3.1:
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export class PlaylistView
|
|||||||
|
|
||||||
@state() private entries: PlaylistEntry[] = [];
|
@state() private entries: PlaylistEntry[] = [];
|
||||||
@state() private loading = true;
|
@state() private loading = true;
|
||||||
|
@state() private refreshing = false;
|
||||||
@state() private creating = false;
|
@state() private creating = false;
|
||||||
@state() private newPlaylistName = '';
|
@state() private newPlaylistName = '';
|
||||||
@state() private contextMenuOpen = false;
|
@state() private contextMenuOpen = false;
|
||||||
@@ -213,6 +214,25 @@ export class PlaylistView
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--yj-text-primary, #fff);
|
color: var(--yj-text-primary, #fff);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-spinner {
|
||||||
|
display: inline-block;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border: 2px solid var(--yj-border-subtle, #555);
|
||||||
|
border-top-color: var(--yj-text-primary, #fff);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.6s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.new-playlist-button {
|
.new-playlist-button {
|
||||||
@@ -644,6 +664,41 @@ export class PlaylistView
|
|||||||
this.restoreScrollPosition();
|
this.restoreScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-fetches playlists without clearing the current view.
|
||||||
|
* Shows a spinner in the header while the fetch is in-flight
|
||||||
|
* and preserves the expanded/collapsed state of each playlist.
|
||||||
|
*/
|
||||||
|
private async refreshPlaylists() {
|
||||||
|
this.refreshing = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const playlists =
|
||||||
|
await this.playlistCtrl.refetch();
|
||||||
|
|
||||||
|
const expandedIDs = new Set(
|
||||||
|
this.entries
|
||||||
|
.filter((e) => e.expanded)
|
||||||
|
.map((e) => e.summary.ID),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.entries = playlists.map((p) => ({
|
||||||
|
summary: p.Summary,
|
||||||
|
expanded: expandedIDs.has(
|
||||||
|
p.Summary.ID,
|
||||||
|
),
|
||||||
|
tracks: p.Tracks ?? [],
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Failed to refresh playlists:',
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
this.refreshing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private handleToggle = (index: number) => {
|
private handleToggle = (index: number) => {
|
||||||
const entry = this.entries[index];
|
const entry = this.entries[index];
|
||||||
|
|
||||||
@@ -796,8 +851,7 @@ export class PlaylistView
|
|||||||
trackIDs,
|
trackIDs,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to remove tracks:',
|
'Failed to remove tracks:',
|
||||||
@@ -954,8 +1008,7 @@ export class PlaylistView
|
|||||||
entry.summary.ID,
|
entry.summary.ID,
|
||||||
payload.filePaths,
|
payload.filePaths,
|
||||||
);
|
);
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to add tracks to playlist:',
|
'Failed to add tracks to playlist:',
|
||||||
@@ -1127,8 +1180,7 @@ export class PlaylistView
|
|||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
await DeletePlaylist(playlistID);
|
await DeletePlaylist(playlistID);
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to delete playlist:',
|
'Failed to delete playlist:',
|
||||||
@@ -1183,8 +1235,7 @@ export class PlaylistView
|
|||||||
entry.summary.ID,
|
entry.summary.ID,
|
||||||
trimmed,
|
trimmed,
|
||||||
);
|
);
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to rename playlist:',
|
'Failed to rename playlist:',
|
||||||
@@ -1205,8 +1256,7 @@ export class PlaylistView
|
|||||||
if (!filePath) return;
|
if (!filePath) return;
|
||||||
|
|
||||||
await ImportPlaylist(filePath);
|
await ImportPlaylist(filePath);
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to import playlist:',
|
'Failed to import playlist:',
|
||||||
@@ -1246,8 +1296,7 @@ export class PlaylistView
|
|||||||
await CreatePlaylist(name);
|
await CreatePlaylist(name);
|
||||||
this.creating = false;
|
this.creating = false;
|
||||||
this.newPlaylistName = '';
|
this.newPlaylistName = '';
|
||||||
this.playlistCtrl.invalidate();
|
await this.refreshPlaylists();
|
||||||
await this.loadPlaylists();
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to create playlist:',
|
'Failed to create playlist:',
|
||||||
@@ -1276,7 +1325,14 @@ export class PlaylistView
|
|||||||
override render() {
|
override render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>Playlists</h2>
|
<h2>
|
||||||
|
Playlists
|
||||||
|
${this.refreshing
|
||||||
|
? html`<span
|
||||||
|
class="header-spinner"
|
||||||
|
></span>`
|
||||||
|
: nothing}
|
||||||
|
</h2>
|
||||||
<div
|
<div
|
||||||
style="display: flex; gap: 8px;"
|
style="display: flex; gap: 8px;"
|
||||||
>
|
>
|
||||||
@@ -1306,7 +1362,8 @@ export class PlaylistView
|
|||||||
${this.creating
|
${this.creating
|
||||||
? this.renderCreateForm()
|
? this.renderCreateForm()
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.loading
|
${this.loading &&
|
||||||
|
this.entries.length === 0
|
||||||
? html`<div class="loading">
|
? html`<div class="loading">
|
||||||
Loading playlists...
|
Loading playlists...
|
||||||
</div>`
|
</div>`
|
||||||
|
|||||||
@@ -65,6 +65,14 @@ export class PlaylistController implements ReactiveController {
|
|||||||
playlistStore.setScrollPosition(offset);
|
playlistStore.setScrollPosition(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// REFETCH
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
async refetch(): Promise<playlist.WithTracks[]> {
|
||||||
|
return playlistStore.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// INVALIDATION
|
// INVALIDATION
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|||||||
@@ -90,6 +90,31 @@ class PlaylistStore {
|
|||||||
this.scrollPosition = offset;
|
this.scrollPosition = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// REFETCH
|
||||||
|
// Fetches fresh data without clearing the cache first, so existing
|
||||||
|
// consumers keep rendering stale data until the new data arrives.
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
async refetch(): Promise<playlist.WithTracks[]> {
|
||||||
|
if (this.playlistsLoading) {
|
||||||
|
return this.waitForPlaylists();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playlistsLoading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result =
|
||||||
|
await GetAllPlaylistsWithTracks();
|
||||||
|
this.playlists = result ?? [];
|
||||||
|
|
||||||
|
return this.playlists;
|
||||||
|
} finally {
|
||||||
|
this.playlistsLoading = false;
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// INVALIDATION
|
// INVALIDATION
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user