SoulSync Discovery tab: open mirror detail modal after refresh
Phase 1c.3 left the click flow at "card shows 'mirrored' + toast",
which felt incomplete — Tidal / LB / Last.fm all open a follow-up
modal after their discovery flow so the user can act on the
results (sync to server playlist, queue downloads, etc.). SoulSync
Discovery skips the discovery phase (tracks pre-matched), so the
natural analog is the mirrored-playlist detail modal — same one
the Mirrored tab opens when you click a row.
- Inline ``fetch('/api/mirror-playlist', ...)`` in place of the
fire-and-forget ``mirrorPlaylist`` helper so we can capture
the returned ``playlist_id`` from the response.
- After successful mirror creation, call
``openMirroredPlaylistModal(playlist_id)`` (exposed by
stats-automations.js) to surface the tracks view.
The card itself keeps the ``♪ N / ✓ N / mirrored`` progress text
so a quick second click can re-refresh without re-opening the
modal each time (just re-runs the generator + re-upserts the
mirror).
This commit is contained in:
parent
bd91c94f92
commit
a8e6432e86
1 changed files with 41 additions and 12 deletions
|
|
@ -181,19 +181,37 @@ async function handleSoulsyncDiscoverySyncCardClick(kind, variant, name, cardEl)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof mirrorPlaylist === 'function') {
|
// POST inline so we can capture the returned mirrored_playlists
|
||||||
mirrorPlaylist(
|
// row id and open the detail modal afterward. ``mirrorPlaylist``
|
||||||
'soulsync_discovery',
|
// (in stats-automations.js) is fire-and-forget and doesn't
|
||||||
syntheticId,
|
// surface the id, which the next step needs.
|
||||||
finalName,
|
const normalizedTracks = mirrorTracks.map(t => ({
|
||||||
mirrorTracks,
|
track_name: t.track_name || '',
|
||||||
{
|
artist_name: t.artist_name || '',
|
||||||
owner: 'SoulSync',
|
album_name: t.album_name || '',
|
||||||
description: `Personalized ${kind}${variant ? ' · ' + variant : ''} — regenerates on Auto-Sync refresh.`,
|
duration_ms: t.duration_ms || 0,
|
||||||
image_url: '',
|
image_url: t.image_url || null,
|
||||||
},
|
source_track_id: t.source_track_id || '',
|
||||||
);
|
extra_data: t.extra_data || null,
|
||||||
|
}));
|
||||||
|
const mirrorResp = await fetch('/api/mirror-playlist', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
source: 'soulsync_discovery',
|
||||||
|
source_playlist_id: syntheticId,
|
||||||
|
name: finalName,
|
||||||
|
tracks: normalizedTracks,
|
||||||
|
description: `Personalized ${kind}${variant ? ' · ' + variant : ''} — regenerates on Auto-Sync refresh.`,
|
||||||
|
owner: 'SoulSync',
|
||||||
|
image_url: '',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const mirrorData = await mirrorResp.json();
|
||||||
|
if (!mirrorData.success) {
|
||||||
|
throw new Error(mirrorData.error || 'Mirror creation failed');
|
||||||
}
|
}
|
||||||
|
const mirroredId = mirrorData.playlist_id;
|
||||||
|
|
||||||
if (progEl) {
|
if (progEl) {
|
||||||
progEl.textContent = `♪ ${tracks.length} / ✓ ${mirrorTracks.length} / mirrored`;
|
progEl.textContent = `♪ ${tracks.length} / ✓ ${mirrorTracks.length} / mirrored`;
|
||||||
|
|
@ -219,6 +237,17 @@ async function handleSoulsyncDiscoverySyncCardClick(kind, variant, name, cardEl)
|
||||||
if (typeof showToast === 'function') {
|
if (typeof showToast === 'function') {
|
||||||
showToast(`Mirrored '${finalName}' with ${mirrorTracks.length} tracks`, 'success');
|
showToast(`Mirrored '${finalName}' with ${mirrorTracks.length} tracks`, 'success');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Open the mirrored-playlist detail modal so the user lands on
|
||||||
|
// the tracks view + can trigger sync / download from there.
|
||||||
|
// Same flow the Mirrored tab uses when clicking a row.
|
||||||
|
if (mirroredId && typeof openMirroredPlaylistModal === 'function') {
|
||||||
|
try {
|
||||||
|
await openMirroredPlaylistModal(mirroredId);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Could not open mirrored playlist detail:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue