diff --git a/frontend/app.js b/frontend/app.js index 6a09f14..43638d3 100755 --- a/frontend/app.js +++ b/frontend/app.js @@ -562,7 +562,7 @@ function showSharedPlaylistModal(name, url, code) { msg.textContent = '\u26a0 ' + ((j && j.error) || 'Could not send'); } else { msg.className = 'share-send-msg ok'; - msg.textContent = `Sent to \u201c${to}\u201d \u2014 they'll see it next time they open YT Player.`; + msg.textContent = `Sent to \u201c${to}\u201d \u2014 they'll get it the next time YT Player is open on their device.`; input.value = ''; } } catch { @@ -727,10 +727,43 @@ async function adoptSharedPlaylistByCode(code, { fromName = '' } = {}) { // Playlists other users sent to this device's linked profile. Offered one at a // time on launch; each is cleared from the server inbox once answered, whether // the user kept it or not, so it is never offered twice. +// +// Launch alone is not enough: an installed PWA is usually resumed from the +// background rather than relaunched, so it also re-checks when it returns to +// the foreground and on a timer while visible (see startPlaylistInboxWatch). +let inboxCheckRunning = false; +let inboxLastCheck = 0; async function checkPlaylistInbox() { - if (!WEB) return; + if (!WEB || inboxCheckRunning) return; const me = data.profile && data.profile.name; if (!me) return; // deliveries are addressed to a profile, not a device + inboxCheckRunning = true; + inboxLastCheck = Date.now(); + try { + await drainPlaylistInbox(me); + } finally { + inboxCheckRunning = false; + } +} + +const INBOX_POLL_MS = 2 * 60 * 1000; +const INBOX_MIN_GAP_MS = 20 * 1000; +function startPlaylistInboxWatch() { + if (!WEB) return; + const maybeCheck = () => { + if (document.visibilityState !== 'visible') return; + if (Date.now() - inboxLastCheck < INBOX_MIN_GAP_MS) return; + // Never pop the prompt over a dialog the user is already in. + const modal = $('modal'); + if (modal && !modal.classList.contains('hidden')) return; + checkPlaylistInbox(); + }; + document.addEventListener('visibilitychange', maybeCheck); + window.addEventListener('focus', maybeCheck); + setInterval(maybeCheck, INBOX_POLL_MS); +} + +async function drainPlaylistInbox(me) { let items = []; try { @@ -5356,6 +5389,7 @@ async function boot() { // After the profile is settled — a delivery is addressed to the profile, so // this must run once we know which one (if any) this device is linked to. await checkPlaylistInbox(); + startPlaylistInboxWatch(); applyAppearance(); updateProfileStatus(); updateLoopRepeatButtons();