add a clear site data button and auto-updates
This commit is contained in:
parent
08b8e52258
commit
37b0747e05
5 changed files with 168 additions and 3 deletions
21
index.html
21
index.html
|
|
@ -3644,6 +3644,27 @@
|
||||||
</div>
|
</div>
|
||||||
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Auto-Update App</span>
|
||||||
|
<span class="description"
|
||||||
|
>Automatically reload when a new version is available</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="pwa-auto-update-toggle" checked />
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Reset Local Data</span>
|
||||||
|
<span class="description"
|
||||||
|
>Clear all local storage and cached data (does not affect cloud sync)</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<button id="reset-local-data-btn" class="btn-secondary danger">Reset</button>
|
||||||
|
</div>
|
||||||
<div class="setting-item">
|
<div class="setting-item">
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span class="label">Backup & Restore</span>
|
<span class="label">Backup & Restore</span>
|
||||||
|
|
|
||||||
37
js/app.js
37
js/app.js
|
|
@ -1,6 +1,13 @@
|
||||||
//js/app.js
|
//js/app.js
|
||||||
import { LosslessAPI } from './api.js';
|
import { LosslessAPI } from './api.js';
|
||||||
import { apiSettings, themeManager, nowPlayingSettings, downloadQualitySettings, sidebarSettings } from './storage.js';
|
import {
|
||||||
|
apiSettings,
|
||||||
|
themeManager,
|
||||||
|
nowPlayingSettings,
|
||||||
|
downloadQualitySettings,
|
||||||
|
sidebarSettings,
|
||||||
|
pwaUpdateSettings,
|
||||||
|
} from './storage.js';
|
||||||
import { UIRenderer } from './ui.js';
|
import { UIRenderer } from './ui.js';
|
||||||
import { Player } from './player.js';
|
import { Player } from './player.js';
|
||||||
import { MultiScrobbler } from './multi-scrobbler.js';
|
import { MultiScrobbler } from './multi-scrobbler.js';
|
||||||
|
|
@ -1471,7 +1478,13 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
} else {
|
} else {
|
||||||
const updateSW = registerSW({
|
const updateSW = registerSW({
|
||||||
onNeedRefresh() {
|
onNeedRefresh() {
|
||||||
showUpdateNotification(() => updateSW(true));
|
if (pwaUpdateSettings.isAutoUpdateEnabled()) {
|
||||||
|
// Auto-update: immediately activate the new service worker
|
||||||
|
updateSW(true);
|
||||||
|
} else {
|
||||||
|
// Show notification with Update button and dismiss option
|
||||||
|
showUpdateNotification(() => updateSW(true));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onOfflineReady() {
|
onOfflineReady() {
|
||||||
console.log('App ready to work offline');
|
console.log('App ready to work offline');
|
||||||
|
|
@ -1558,6 +1571,12 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function showUpdateNotification(updateCallback) {
|
function showUpdateNotification(updateCallback) {
|
||||||
|
// Remove any existing update notification
|
||||||
|
const existingNotification = document.querySelector('.update-notification');
|
||||||
|
if (existingNotification) {
|
||||||
|
existingNotification.remove();
|
||||||
|
}
|
||||||
|
|
||||||
const notification = document.createElement('div');
|
const notification = document.createElement('div');
|
||||||
notification.className = 'update-notification';
|
notification.className = 'update-notification';
|
||||||
notification.innerHTML = `
|
notification.innerHTML = `
|
||||||
|
|
@ -1565,7 +1584,15 @@ function showUpdateNotification(updateCallback) {
|
||||||
<strong>Update Available</strong>
|
<strong>Update Available</strong>
|
||||||
<p>A new version of Monochrome is available.</p>
|
<p>A new version of Monochrome is available.</p>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-primary" id="update-now-btn">Update Now</button>
|
<div class="update-notification-actions">
|
||||||
|
<button class="btn-primary" id="update-now-btn">Update Now</button>
|
||||||
|
<button class="btn-icon" id="dismiss-update-btn" title="Dismiss">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
document.body.appendChild(notification);
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
|
|
@ -1578,6 +1605,10 @@ function showUpdateNotification(updateCallback) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('dismiss-update-btn').addEventListener('click', () => {
|
||||||
|
notification.remove();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMissingTracksNotification(missingTracks) {
|
function showMissingTracksNotification(missingTracks) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import {
|
||||||
monoAudioSettings,
|
monoAudioSettings,
|
||||||
exponentialVolumeSettings,
|
exponentialVolumeSettings,
|
||||||
audioEffectsSettings,
|
audioEffectsSettings,
|
||||||
|
pwaUpdateSettings,
|
||||||
} from './storage.js';
|
} from './storage.js';
|
||||||
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
import { audioContextManager, EQ_PRESETS } from './audio-context.js';
|
||||||
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
import { getButterchurnPresets } from './visualizers/butterchurn.js';
|
||||||
|
|
@ -1777,6 +1778,73 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PWA Auto-Update Toggle
|
||||||
|
const pwaAutoUpdateToggle = document.getElementById('pwa-auto-update-toggle');
|
||||||
|
if (pwaAutoUpdateToggle) {
|
||||||
|
pwaAutoUpdateToggle.checked = pwaUpdateSettings.isAutoUpdateEnabled();
|
||||||
|
pwaAutoUpdateToggle.addEventListener('change', (e) => {
|
||||||
|
pwaUpdateSettings.setAutoUpdateEnabled(e.target.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset Local Data Button
|
||||||
|
const resetLocalDataBtn = document.getElementById('reset-local-data-btn');
|
||||||
|
if (resetLocalDataBtn) {
|
||||||
|
resetLocalDataBtn.addEventListener('click', async () => {
|
||||||
|
if (
|
||||||
|
confirm(
|
||||||
|
'WARNING: This will clear all local data including settings, cache, and library.\n\nAre you sure you want to continue?\n\n(Cloud-synced data will not be affected)'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
// Clear all localStorage
|
||||||
|
const keysToPreserve = [];
|
||||||
|
// Optionally preserve certain keys if needed
|
||||||
|
|
||||||
|
// Get all keys
|
||||||
|
const allKeys = Object.keys(localStorage);
|
||||||
|
|
||||||
|
// Clear each key except preserved ones
|
||||||
|
allKeys.forEach((key) => {
|
||||||
|
if (!keysToPreserve.includes(key)) {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear IndexedDB - try to clear individual stores, fallback to deleting database
|
||||||
|
try {
|
||||||
|
const stores = ['tracks', 'albums', 'artists', 'playlists', 'settings', 'history'];
|
||||||
|
for (const storeName of stores) {
|
||||||
|
try {
|
||||||
|
await db.performTransaction(storeName, 'readwrite', (store) => store.clear());
|
||||||
|
} catch (e) {
|
||||||
|
// Store might not exist, continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (dbError) {
|
||||||
|
console.log('Could not clear IndexedDB stores:', dbError);
|
||||||
|
// Try to delete the entire database as fallback
|
||||||
|
try {
|
||||||
|
const deleteRequest = indexedDB.deleteDatabase('monochrome-music');
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
deleteRequest.onsuccess = resolve;
|
||||||
|
deleteRequest.onerror = reject;
|
||||||
|
});
|
||||||
|
} catch (deleteError) {
|
||||||
|
console.log('Could not delete IndexedDB:', deleteError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alert('All local data has been cleared. The app will now reload.');
|
||||||
|
window.location.reload();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to reset local data:', error);
|
||||||
|
alert('Failed to reset local data: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Font Settings
|
// Font Settings
|
||||||
initializeFontSettings();
|
initializeFontSettings();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1703,3 +1703,20 @@ export const fontSettings = {
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const pwaUpdateSettings = {
|
||||||
|
STORAGE_KEY: 'pwa-auto-update-enabled',
|
||||||
|
|
||||||
|
isAutoUpdateEnabled() {
|
||||||
|
try {
|
||||||
|
// Default to true (auto-update) if not set
|
||||||
|
return localStorage.getItem(this.STORAGE_KEY) !== 'false';
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setAutoUpdateEnabled(enabled) {
|
||||||
|
localStorage.setItem(this.STORAGE_KEY, enabled ? 'true' : 'false');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
||||||
28
styles.css
28
styles.css
|
|
@ -3430,6 +3430,34 @@ input:checked + .slider::before {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.update-notification-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-notification-actions .btn-icon {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--muted-foreground);
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-notification-actions .btn-icon:hover {
|
||||||
|
background: var(--secondary);
|
||||||
|
color: var(--foreground);
|
||||||
|
border-color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
.close-shortcuts {
|
.close-shortcuts {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue