Save worktree changes
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// Show or hide the offline status banner.
|
||||
function setOfflineBannerVisible(visible, message) {
|
||||
var normalizedVisible = Boolean(visible);
|
||||
var bannerMessage = String(message || 'Offline mode: using cached playlist.').trim();
|
||||
if (normalizedVisible) {
|
||||
if (!offlineBanner) {
|
||||
offlineBanner = document.createElement('div');
|
||||
offlineBanner.className = 'player-offline-banner';
|
||||
offlineBanner.style.position = 'fixed';
|
||||
offlineBanner.style.right = '0';
|
||||
offlineBanner.style.bottom = '0';
|
||||
offlineBanner.style.left = 'auto';
|
||||
offlineBanner.style.top = 'auto';
|
||||
offlineBanner.style.width = '1.25rem';
|
||||
offlineBanner.style.height = '1.25rem';
|
||||
offlineBanner.style.zIndex = '9999';
|
||||
offlineBanner.style.background = 'linear-gradient(135deg, #ff4d4f 0%, #b00020 100%)';
|
||||
offlineBanner.style.clipPath = 'circle(100% at 100% 100%)';
|
||||
offlineBanner.style.webkitClipPath = 'circle(100% at 100% 100%)';
|
||||
offlineBanner.style.boxShadow = '0 0 0 1px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.18)';
|
||||
offlineBanner.style.pointerEvents = 'none';
|
||||
document.body.appendChild(offlineBanner);
|
||||
}
|
||||
offlineBanner.textContent = '';
|
||||
offlineBanner.setAttribute('aria-label', bannerMessage);
|
||||
offlineBanner.setAttribute('role', 'img');
|
||||
offlineBanner.title = bannerMessage;
|
||||
offlineBannerVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
offlineBannerVisible = false;
|
||||
if (offlineBanner && offlineBanner.parentNode) {
|
||||
offlineBanner.parentNode.removeChild(offlineBanner);
|
||||
}
|
||||
offlineBanner = null;
|
||||
}
|
||||
|
||||
// Update the offline banner based on connectivity or playlist availability.
|
||||
function syncOfflineBanner() {
|
||||
if (!window.navigator.onLine) {
|
||||
setOfflineBannerVisible(true, 'Offline mode: using cached playlist.');
|
||||
return;
|
||||
}
|
||||
if (offlineBannerVisible) {
|
||||
setOfflineBannerVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear any pending playlist refresh retry.
|
||||
function clearRefreshRetry() {
|
||||
if (refreshRetryTimer) {
|
||||
window.clearTimeout(refreshRetryTimer);
|
||||
refreshRetryTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Retry playlist refresh with a short backoff while the player is offline.
|
||||
function scheduleRefreshRetry() {
|
||||
if (refreshRetryTimer) {
|
||||
return;
|
||||
}
|
||||
if (window.navigator.onLine === false) {
|
||||
refreshRetryDelayMs = refreshRetryDelayMs ? Math.min(refreshRetryDelayMs * 2, 15000) : 3000;
|
||||
} else {
|
||||
refreshRetryDelayMs = refreshRetryDelayMs ? Math.min(refreshRetryDelayMs * 2, 8000) : 3000;
|
||||
}
|
||||
refreshRetryTimer = window.setTimeout(function () {
|
||||
refreshRetryTimer = null;
|
||||
refresh();
|
||||
}, refreshRetryDelayMs);
|
||||
}
|
||||
|
||||
function clearScreenWakeLockRetry() {
|
||||
if (screenWakeLockRetryTimer) {
|
||||
window.clearTimeout(screenWakeLockRetryTimer);
|
||||
screenWakeLockRetryTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function supportsScreenWakeLock() {
|
||||
return Boolean(window.navigator && window.navigator.wakeLock && typeof window.navigator.wakeLock.request === 'function');
|
||||
}
|
||||
|
||||
function scheduleScreenWakeLockRetry() {
|
||||
if (screenWakeLockRetryTimer) {
|
||||
return;
|
||||
}
|
||||
if (!supportsScreenWakeLock() || document.visibilityState !== 'visible') {
|
||||
return;
|
||||
}
|
||||
screenWakeLockRetryTimer = window.setTimeout(function () {
|
||||
screenWakeLockRetryTimer = null;
|
||||
acquireScreenWakeLock();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function releaseScreenWakeLock() {
|
||||
if (screenWakeLock && typeof screenWakeLock.release === 'function') {
|
||||
try {
|
||||
screenWakeLock.release();
|
||||
} catch (_error) {
|
||||
// ignore wake lock release errors
|
||||
}
|
||||
}
|
||||
screenWakeLock = null;
|
||||
screenWakeLockRequestPromise = null;
|
||||
clearScreenWakeLockRetry();
|
||||
}
|
||||
|
||||
function acquireScreenWakeLock() {
|
||||
if (!supportsScreenWakeLock() || document.visibilityState !== 'visible') {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
if (screenWakeLockRequestPromise) {
|
||||
return screenWakeLockRequestPromise;
|
||||
}
|
||||
if (screenWakeLock && screenWakeLock.released === false) {
|
||||
return Promise.resolve(screenWakeLock);
|
||||
}
|
||||
|
||||
screenWakeLockRequestPromise = window.navigator.wakeLock.request('screen').then(function (sentinel) {
|
||||
screenWakeLock = sentinel;
|
||||
screenWakeLock.addEventListener('release', function () {
|
||||
screenWakeLock = null;
|
||||
if (document.visibilityState === 'visible') {
|
||||
scheduleScreenWakeLockRetry();
|
||||
}
|
||||
});
|
||||
clearScreenWakeLockRetry();
|
||||
return screenWakeLock;
|
||||
}).catch(function (error) {
|
||||
screenWakeLock = null;
|
||||
if (error && error.name !== 'NotAllowedError') {
|
||||
scheduleScreenWakeLockRetry();
|
||||
}
|
||||
return null;
|
||||
}).finally(function () {
|
||||
screenWakeLockRequestPromise = null;
|
||||
});
|
||||
|
||||
return screenWakeLockRequestPromise;
|
||||
}
|
||||
|
||||
function syncScreenWakeLock() {
|
||||
if (!supportsScreenWakeLock()) {
|
||||
return;
|
||||
}
|
||||
if (document.visibilityState === 'visible') {
|
||||
acquireScreenWakeLock();
|
||||
return;
|
||||
}
|
||||
releaseScreenWakeLock();
|
||||
}
|
||||
|
||||
// Clear the retry cadence after a successful refresh.
|
||||
function markRefreshHealthy() {
|
||||
refreshRetryDelayMs = 0;
|
||||
clearRefreshRetry();
|
||||
}
|
||||
Reference in New Issue
Block a user