perf(library): hand the batch dialog the rows the view already has
Fetching whole tracks back for a dialog that reads only fields the rows in hand already carry cost 3 s and 700 ms of blocked main thread on a "select all" over 50 000 tracks — the regression #281 introduced by dropping four fields from the list. `showBatchTrackDetails` takes the rows; the Tracks view passes its own, and the views that hold no library rows (Explore, the queue, playlists) keep the path form over `trackCache`. The batch view's merged-fields extractors read only list fields already, and a cover tier the list does not carry falls back to the one it does. Measured on 50 000 tracks: 50.4 ms, from 87.9 ms before #281. Refs #281
This commit is contained in:
1 parent
7a1412383f
commit
3d9828b847
3 files changed
+70
-34
No files matched your search
@@ -24,6 +24,7 @@ type TrackMBIDs = library.TrackMBIDs;
|
||||
import { ImageFilePicker, ReadFile } from '@go/frontendutil/frontendutil.js';
|
||||
import { trackCache } from '../../store/track-cache';
|
||||
import { batchCoverArt } from '@utils/track-details-opener.js';
|
||||
import type { ListTrack } from '@utils/track-table';
|
||||
import { EventsOn } from '@runtime/runtime';
|
||||
import { Events } from '../../events';
|
||||
|
||||
@@ -88,7 +89,11 @@ export class TrackDetails extends LitElement {
|
||||
|
||||
// -- Batch-specific state --
|
||||
@state() private batchMode = false;
|
||||
@state() private batchTracks: library.Track[] = [];
|
||||
// `ListTrack`, not `Track`: every field the merged-values view reads
|
||||
// is on the list's rows, so a caller that is already showing them
|
||||
// can open this dialog without fetching anything (#281). The
|
||||
// openers that hold no rows ask `trackCache` instead.
|
||||
@state() private batchTracks: ListTrack[] = [];
|
||||
@state() private batchFilePaths: string[] = [];
|
||||
@state() private batchCoverArtMixed = false;
|
||||
@state() private batchProgress: {
|
||||
@@ -136,12 +141,12 @@ export class TrackDetails extends LitElement {
|
||||
|
||||
/** Open the dialog for batch editing multiple tracks. */
|
||||
showBatch(
|
||||
tracks: library.Track[],
|
||||
tracks: readonly ListTrack[],
|
||||
coverArt: CoverArtUrls | null,
|
||||
coverArtMixed: boolean,
|
||||
): void {
|
||||
this.batchMode = true;
|
||||
this.batchTracks = tracks;
|
||||
this.batchTracks = [...tracks];
|
||||
this.batchFilePaths = tracks.map(
|
||||
(t) => t.FilePath,
|
||||
);
|
||||
@@ -2133,7 +2138,7 @@ export class TrackDetails extends LitElement {
|
||||
key: string;
|
||||
label: string;
|
||||
type: 'text' | 'number';
|
||||
extract: (t: library.Track) => string;
|
||||
extract: (t: ListTrack) => string;
|
||||
}> = [
|
||||
{
|
||||
key: 'title',
|
||||
@@ -2217,7 +2222,7 @@ export class TrackDetails extends LitElement {
|
||||
private countDistinctValues(key: string): number {
|
||||
const extractMap: Record<
|
||||
string,
|
||||
(t: library.Track) => string
|
||||
(t: ListTrack) => string
|
||||
> = {
|
||||
title: (t) => t.TrackName ?? '',
|
||||
artist: (t) => t.ArtistName ?? '',
|
||||
@@ -2241,7 +2246,7 @@ export class TrackDetails extends LitElement {
|
||||
if (!extract) return 0;
|
||||
|
||||
const unique = new Set(
|
||||
this.batchTracks.map(extract),
|
||||
this.batchTracks.map((t) => extract(t)),
|
||||
);
|
||||
|
||||
return unique.size;
|
||||
|
||||
@@ -80,8 +80,8 @@ import { describeError } from '@utils/describe-error';
|
||||
import { notificationStore } from '@store/notification-store';
|
||||
import { confirmAction } from '@components/confirm-dialog/confirm-dialog';
|
||||
import { RemoveFromLibrary } from '@go/library/library.js';
|
||||
import { showTrackDetailsForPath, showBatchTrackDetailsForPaths } from '@utils/track-details-opener.js';
|
||||
import { tracksByFilePath } from '@utils/track-index.js';
|
||||
import { showTrackDetailsForPath, showBatchTrackDetails } from '@utils/track-details-opener.js';
|
||||
import { tracksByFilePath, tracksForPaths } from '@utils/track-index.js';
|
||||
import '@components/playlist-picker/playlist-picker.js';
|
||||
import type { TrackDetails } from '@components/track-details/track-details.js';
|
||||
import {
|
||||
@@ -2141,9 +2141,11 @@ export class TrackList
|
||||
private async openBatchTrackDetails(
|
||||
filePaths: string[],
|
||||
) {
|
||||
await showBatchTrackDetailsForPaths(
|
||||
// The rows in hand are what the dialog reads, so a "select all"
|
||||
// on 50 000 tracks opens it without asking the backend for them.
|
||||
await showBatchTrackDetails(
|
||||
() => this.trackDetailsDialog,
|
||||
filePaths,
|
||||
tracksForPaths(this.tracks, filePaths),
|
||||
() => void this.openBatchTrackDetails(filePaths),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user