diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-15 17:01:06 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-15 17:01:06 +0200 |
| commit | 459fc93e98f23e326c2fa77fe86ba74c2bae77f0 (patch) | |
| tree | f506a127cc3e0779c4d8cf9f27fe659d81bb787e /packages/meshbay-hub/src/meshbay_hub/static/music-app.js | |
| parent | fce0942cb7d81dbdbae70e50b0164fead7e8bc49 (diff) | |
| download | meshbay-459fc93e98f23e326c2fa77fe86ba74c2bae77f0.tar.gz | |
feat(hub): Videos and Music list their cards a page at a time
Previous/next arrows in the pinned toolbar, on group pages and in Search.
Page size is an account preference (Settings → Defaults), 50 by default,
10 to 200 in steps of 10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/music-app.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/music-app.js | 44 |
1 files changed, 34 insertions, 10 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js index f622b5d..60a7b00 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-app.js @@ -6,6 +6,7 @@ import { Icon } from './icon.js'; import { MediaThumb, LazyTile } from './video-app.js'; import { formatTime } from './music-player.js'; import { SourceTag } from './group-name.js'; +import { usePager, Pager, pageSizeFrom } from './pager.js'; // -- Music -------------------------------------------------------------------- // @@ -324,8 +325,16 @@ function MusicDetailModal({ album, transportRef, gekRef, musicbrainzEnabled, onC // -- Mode A: album grid ----------------------------------------------------- -function AlbumGrid({ artists, transportRef, gekRef, musicbrainzEnabled, onPlayQueue }) { +// `units` is one page of `{ artist, album }`. An artist whose albums straddle +// two pages gets its heading on both. +function AlbumGrid({ units, transportRef, gekRef, musicbrainzEnabled, onPlayQueue }) { const [detail, setDetail] = useState(null); // the album object + const artists = []; + for (const u of units) { + const prev = artists[artists.length - 1]; + if (prev && prev.artist === u.artist) prev.albums.push(u.album); + else artists.push({ artist: u.artist, albums: [u.album] }); + } return html` ${artists.map((a) => html` @@ -435,12 +444,8 @@ function FlatArtistFolder({ artist, onPlayQueue }) { `; } -function FlatList({ tracks, artists, onPlayQueue }) { - const items = [ - ...tracks.map((tr) => ({ key: tr.display_title || tr.name, kind: 'track', track: tr })), - ...artists.map((a) => ({ key: a.artist, kind: 'artist', artist: a })), - ].sort((a, b) => a.key.localeCompare(b.key)); - +// `items` is one page of rows, already sorted by MusicApp. +function FlatList({ items, onPlayQueue }) { return html` <div class="video-flat-list"> ${items.map((it) => it.kind === 'track' @@ -456,7 +461,7 @@ function FlatList({ tracks, artists, onPlayQueue }) { function MusicApp({ groupId, transportRef, gekRef, status, entries, availableEntries, musicDirectories, musicbrainzConfig, onPlayQueue, - hideFilter, + hideFilter, userPrefs, pageResetKey, }) { const [mode, setMode] = useState(loadViewMode); const [filter, setFilter] = useState(''); @@ -488,6 +493,24 @@ function MusicApp({ const filteredTracks = useMemo(() => (!needle ? tracks : tracks.filter( (tr) => (tr.display_title || tr.name).toLowerCase().includes(needle))), [tracks, needle]); + // What this mode draws, in drawing order: albums under their artists in the + // grid, and in the flat list loose tracks and artist folders sorted together. + const units = useMemo(() => { + if (mode === 'grid') { + return filteredArtists.flatMap((a) => a.albums.map((album) => ({ artist: a.artist, album }))); + } + return [ + ...filteredTracks.map((tr) => ({ key: tr.display_title || tr.name, kind: 'track', track: tr })), + ...filteredArtists.map((a) => ({ key: a.artist, kind: 'artist', artist: a })), + ].sort((a, b) => a.key.localeCompare(b.key)); + }, [mode, filteredArtists, filteredTracks]); + + const pager = usePager(units.length, pageSizeFrom(userPrefs), + `${groupId}|${mode}|${needle}|${pageResetKey || ''}`); + const pageUnits = useMemo(() => { + return units.slice(pager.start, pager.end); + }, [units, pager.start, pager.end]); + const empty = albums.length === 0 && tracks.length === 0; return html` @@ -510,6 +533,7 @@ function MusicApp({ onClick=${() => setModeAndSave('flat')}> ${t('music.mode_flat')} </button> + <${Pager} pager=${pager} /> ${!hideFilter && html`<div class="tb-search"> <${Icon} name="search" /> <input type="text" placeholder="${t('group.filter')}" @@ -521,9 +545,9 @@ function MusicApp({ <p class="page-message">${t('group.empty_filter')}</p> `} ${!empty && mode === 'grid' - ? html`<${AlbumGrid} artists=${filteredArtists} transportRef=${transportRef} gekRef=${gekRef} + ? html`<${AlbumGrid} units=${pageUnits} transportRef=${transportRef} gekRef=${gekRef} musicbrainzEnabled=${musicbrainzEnabled} onPlayQueue=${onPlayQueue} />` - : !empty && html`<${FlatList} tracks=${filteredTracks} artists=${filteredArtists} + : !empty && html`<${FlatList} items=${pageUnits} onPlayQueue=${onPlayQueue} />`} `} `; |