feat(quick-8): wire duplicate detection into playlist-picker and playlist-view
- playlist-picker checks for duplicates before adding tracks - playlist-view drag-drop handler checks for duplicates before adding - Both show duplicate-tracks-dialog when duplicates found - Non-duplicate tracks added directly when no duplicates exist
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { LitElement, html, css, nothing } from 'lit';
|
import { LitElement, html, css, nothing } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state, query } from 'lit/decorators.js';
|
||||||
import { EventsOn } from '@runtime/runtime';
|
import { EventsOn } from '@runtime/runtime';
|
||||||
|
|
||||||
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
||||||
@@ -9,9 +9,12 @@ import {
|
|||||||
GetAllPlaylists,
|
GetAllPlaylists,
|
||||||
AddTracksToPlaylist,
|
AddTracksToPlaylist,
|
||||||
CreatePlaylistWithTracks,
|
CreatePlaylistWithTracks,
|
||||||
|
FindDuplicateTracksInPlaylist,
|
||||||
} from '@go/playlist/Service';
|
} from '@go/playlist/Service';
|
||||||
import { Events } from '../../events';
|
import { Events } from '../../events';
|
||||||
import type { playlist } from '@go/models';
|
import type { playlist } from '@go/models';
|
||||||
|
import '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js';
|
||||||
|
import type { DuplicateTracksDialog } from '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reusable playlist picker that displays existing playlists
|
* A reusable playlist picker that displays existing playlists
|
||||||
@@ -26,6 +29,9 @@ export class PlaylistPicker extends LitElement {
|
|||||||
@property({ type: Array }) filePaths: string[] = [];
|
@property({ type: Array }) filePaths: string[] = [];
|
||||||
private cancelScanComplete?: () => void;
|
private cancelScanComplete?: () => void;
|
||||||
|
|
||||||
|
@query('duplicate-tracks-dialog')
|
||||||
|
private duplicateDialog!: DuplicateTracksDialog;
|
||||||
|
|
||||||
@state() private mode: 'list' | 'create' = 'list';
|
@state() private mode: 'list' | 'create' = 'list';
|
||||||
@state() private playlists: playlist.Summary[] = [];
|
@state() private playlists: playlist.Summary[] = [];
|
||||||
@state() private newPlaylistName = '';
|
@state() private newPlaylistName = '';
|
||||||
@@ -161,6 +167,23 @@ export class PlaylistPicker extends LitElement {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const result = await FindDuplicateTracksInPlaylist(
|
||||||
|
playlistId,
|
||||||
|
this.filePaths,
|
||||||
|
);
|
||||||
|
const duplicates = result.Duplicates ?? [];
|
||||||
|
const unique = result.Unique ?? [];
|
||||||
|
|
||||||
|
if (duplicates.length > 0) {
|
||||||
|
// Show dialog — it handles adding tracks and dispatching completion.
|
||||||
|
this.loading = false;
|
||||||
|
await this.updateComplete;
|
||||||
|
this.duplicateDialog.show(playlistId, duplicates, unique);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No duplicates — add all directly.
|
||||||
await AddTracksToPlaylist(playlistId, this.filePaths);
|
await AddTracksToPlaylist(playlistId, this.filePaths);
|
||||||
this.dispatchComplete();
|
this.dispatchComplete();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -239,11 +262,14 @@ export class PlaylistPicker extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override render() {
|
override render() {
|
||||||
if (this.mode === 'create') {
|
return html`
|
||||||
return this.renderCreateForm();
|
${this.mode === 'create'
|
||||||
}
|
? this.renderCreateForm()
|
||||||
|
: this.renderPlaylistList()}
|
||||||
return this.renderPlaylistList();
|
<duplicate-tracks-dialog
|
||||||
|
@playlist-action-complete=${this.dispatchComplete}
|
||||||
|
></duplicate-tracks-dialog>
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderPlaylistList() {
|
private renderPlaylistList() {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
RenamePlaylist,
|
RenamePlaylist,
|
||||||
ImportPlaylists,
|
ImportPlaylists,
|
||||||
RemovePhantomTracks,
|
RemovePhantomTracks,
|
||||||
|
FindDuplicateTracksInPlaylist,
|
||||||
} from '@go/playlist/Service';
|
} from '@go/playlist/Service';
|
||||||
import { PlaylistFilePicker } from '@go/frontendutil/FrontendUtil';
|
import { PlaylistFilePicker } from '@go/frontendutil/FrontendUtil';
|
||||||
import type { playlist } from '@go/models';
|
import type { playlist } from '@go/models';
|
||||||
@@ -48,6 +49,8 @@ import type { TrackDetails } from '@components/track-details/track-details.js';
|
|||||||
import type { CoverArtUrls } from '@components/track-details/track-details.js';
|
import type { CoverArtUrls } from '@components/track-details/track-details.js';
|
||||||
import '@components/phantom-resolver/phantom-resolver.js';
|
import '@components/phantom-resolver/phantom-resolver.js';
|
||||||
import type { PhantomResolver } from '@components/phantom-resolver/phantom-resolver.js';
|
import type { PhantomResolver } from '@components/phantom-resolver/phantom-resolver.js';
|
||||||
|
import '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js';
|
||||||
|
import type { DuplicateTracksDialog } from '@components/duplicate-tracks-dialog/duplicate-tracks-dialog.js';
|
||||||
|
|
||||||
const SCROLL_DEBOUNCE_MS = 100;
|
const SCROLL_DEBOUNCE_MS = 100;
|
||||||
|
|
||||||
@@ -253,6 +256,9 @@ export class PlaylistView
|
|||||||
@query('phantom-resolver')
|
@query('phantom-resolver')
|
||||||
private phantomResolver!: PhantomResolver;
|
private phantomResolver!: PhantomResolver;
|
||||||
|
|
||||||
|
@query('duplicate-tracks-dialog')
|
||||||
|
private duplicateDialog!: DuplicateTracksDialog;
|
||||||
|
|
||||||
private closePlaylistCtxMenuHandler =
|
private closePlaylistCtxMenuHandler =
|
||||||
() => this.closePlaylistContextMenu();
|
() => this.closePlaylistContextMenu();
|
||||||
|
|
||||||
@@ -1821,6 +1827,24 @@ export class PlaylistView
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const result = await FindDuplicateTracksInPlaylist(
|
||||||
|
entry.summary.ID,
|
||||||
|
payload.filePaths,
|
||||||
|
);
|
||||||
|
const duplicates = result.Duplicates ?? [];
|
||||||
|
const unique = result.Unique ?? [];
|
||||||
|
|
||||||
|
if (duplicates.length > 0) {
|
||||||
|
await this.updateComplete;
|
||||||
|
this.duplicateDialog.show(
|
||||||
|
entry.summary.ID,
|
||||||
|
duplicates,
|
||||||
|
unique,
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await AddTracksToPlaylist(
|
await AddTracksToPlaylist(
|
||||||
entry.summary.ID,
|
entry.summary.ID,
|
||||||
payload.filePaths,
|
payload.filePaths,
|
||||||
@@ -2785,6 +2809,10 @@ export class PlaylistView
|
|||||||
@phantom-resolved=${() =>
|
@phantom-resolved=${() =>
|
||||||
this.refreshPlaylists()}
|
this.refreshPlaylists()}
|
||||||
></phantom-resolver>
|
></phantom-resolver>
|
||||||
|
<duplicate-tracks-dialog
|
||||||
|
@playlist-action-complete=${() =>
|
||||||
|
this.refreshPlaylists()}
|
||||||
|
></duplicate-tracks-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user