new: added default firebase instance, reworked settings layout
This commit is contained in:
parent
2a98654e54
commit
060d4762cc
6 changed files with 338 additions and 188 deletions
384
index.html
384
index.html
|
|
@ -282,190 +282,212 @@
|
||||||
<div id="page-settings" class="page">
|
<div id="page-settings" class="page">
|
||||||
<h2 class="section-title">Settings</h2>
|
<h2 class="section-title">Settings</h2>
|
||||||
<div class="settings-list">
|
<div class="settings-list">
|
||||||
<div class="setting-item">
|
<div class="settings-group">
|
||||||
<div class="info">
|
<div class="setting-item">
|
||||||
<span class="label">Theme</span>
|
|
||||||
<span class="description">Choose your preferred color scheme</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="theme-picker" id="theme-picker">
|
|
||||||
<div class="theme-option" data-theme="system">System</div>
|
|
||||||
<div class="theme-option" data-theme="light">Light</div>
|
|
||||||
<div class="theme-option" data-theme="dark">Dark</div>
|
|
||||||
<div class="theme-option" data-theme="monochrome">Black</div>
|
|
||||||
<div class="theme-option" data-theme="ocean">Ocean</div>
|
|
||||||
<div class="theme-option" data-theme="purple">Purple</div>
|
|
||||||
<div class="theme-option" data-theme="forest">Forest</div>
|
|
||||||
<div class="theme-option" data-theme="custom">Custom</div>
|
|
||||||
</div>
|
|
||||||
<div class="custom-theme-editor" id="custom-theme-editor">
|
|
||||||
<h4>Custom Theme</h4>
|
|
||||||
<div class="theme-color-grid" id="theme-color-grid"></div>
|
|
||||||
<div class="theme-actions">
|
|
||||||
<button class="btn-secondary" id="apply-custom-theme">Apply Theme</button>
|
|
||||||
<button class="btn-secondary" id="reset-custom-theme">Reset</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Album Cover Background</span>
|
|
||||||
<span class="description">Use the album cover as a blurred background on album pages and as primary color</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle-switch">
|
|
||||||
<input type="checkbox" id="album-background-toggle">
|
|
||||||
<span class="slider"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Last.fm Scrobbling</span>
|
|
||||||
<span class="description" id="lastfm-status">Connect your Last.fm account to scrobble tracks</span>
|
|
||||||
</div>
|
|
||||||
<div id="lastfm-controls">
|
|
||||||
<button id="lastfm-connect-btn" class="btn-secondary">Connect Last.fm</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item" id="lastfm-toggle-setting" style="display: none;">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Enable Scrobbling</span>
|
|
||||||
<span class="description">Automatically scrobble played tracks</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle-switch">
|
|
||||||
<input type="checkbox" id="lastfm-toggle">
|
|
||||||
<span class="slider"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Firebase Configuration</span>
|
|
||||||
<span class="description">Paste your Firebase Config JSON here to enable sync.</span>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; flex-direction: column; gap: 0.5rem; width: 100%; max-width: 400px;">
|
|
||||||
<textarea id="firebase-config-input" class="template-input" rows="5" placeholder='{ "apiKey": "...", "authDomain": "...", ... }'></textarea>
|
|
||||||
<div style="display: flex; gap: 0.5rem;">
|
|
||||||
<button id="save-firebase-config-btn" class="btn-secondary">Save & Reload</button>
|
|
||||||
<button id="share-firebase-config-btn" class="btn-secondary">Share</button>
|
|
||||||
<button id="clear-firebase-config-btn" class="btn-secondary danger">Clear Config</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Sync & Backup (Beta)</span>
|
|
||||||
<span class="description" id="firebase-status">Sync your library across devices</span>
|
|
||||||
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;">
|
|
||||||
<img id="firebase-user-avatar" src="" style="width: 24px; height: 24px; border-radius: 50%; display: none;" onerror="this.style.display='none'" onload="this.style.display='block'">
|
|
||||||
<span id="firebase-user-name" style="font-size: 0.85rem; color: var(--foreground);"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="firebase-controls">
|
|
||||||
<button id="firebase-connect-btn" class="btn-secondary">Connect with Google</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Audio Quality</span>
|
|
||||||
<span class="description">Quality for streaming and downloads</span>
|
|
||||||
</div>
|
|
||||||
<select id="quality-setting">
|
|
||||||
<option value="LOSSLESS">FLAC (Lossless)</option>
|
|
||||||
<option value="HIGH">AAC 320kbps</option>
|
|
||||||
<option value="LOW">AAC 96kbps</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Now Playing View Mode</span>
|
|
||||||
<span class="description">Choose what shows when you click the album art</span>
|
|
||||||
</div>
|
|
||||||
<select id="now-playing-mode">
|
|
||||||
<option value="album">Show Album</option>
|
|
||||||
<option value="cover">Enlarged Cover</option>
|
|
||||||
<option value="lyrics">Lyrics Panel</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Track List Actions</span>
|
|
||||||
<span class="description">Choose between a dropdown menu or inline buttons for track actions</span>
|
|
||||||
</div>
|
|
||||||
<select id="track-list-actions-mode">
|
|
||||||
<option value="dropdown">Dropdown Menu</option>
|
|
||||||
<option value="inline">Inline Buttons</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Download Lyrics</span>
|
|
||||||
<span class="description">Include .lrc files when downloading tracks/albums</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle-switch">
|
|
||||||
<input type="checkbox" id="download-lyrics-toggle">
|
|
||||||
<span class="slider"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Gapless Playback</span>
|
|
||||||
<span class="description">Play audio without interruption between tracks</span>
|
|
||||||
</div>
|
|
||||||
<label class="toggle-switch">
|
|
||||||
<input type="checkbox" checked>
|
|
||||||
<span class="slider"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Filename Template</span>
|
|
||||||
<span class="description">Customize download filenames. Available: {trackNumber}, {artist}, {title}, {album}</span>
|
|
||||||
</div>
|
|
||||||
<input type="text" id="filename-template" class="template-input" placeholder="{trackNumber} - {artist} - {title}">
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">ZIP Folder Template</span>
|
|
||||||
<span class="description">Customize album folder names. Available: {albumTitle}, {albumArtist}, {year}</span>
|
|
||||||
</div>
|
|
||||||
<input type="text" id="zip-folder-template" class="template-input" placeholder="{albumTitle} - {albumArtist} - monochrome.tf">
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Keyboard Shortcuts</span>
|
|
||||||
<span class="description">View available keyboard shortcuts</span>
|
|
||||||
</div>
|
|
||||||
<button id="show-shortcuts-btn" class="btn-secondary">Show Shortcuts</button>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Cache</span>
|
|
||||||
<span class="description" id="cache-info">Stores API responses to reduce requests</span>
|
|
||||||
</div>
|
|
||||||
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
|
||||||
</div>
|
|
||||||
<div class="setting-item">
|
|
||||||
<div class="info">
|
|
||||||
<span class="label">Backup & Restore</span>
|
|
||||||
<span class="description">Export or import your library and history as JSON</span>
|
|
||||||
</div>
|
|
||||||
<div style="display: flex; gap: 0.5rem;">
|
|
||||||
<button id="export-library-btn" class="btn-secondary">Export</button>
|
|
||||||
<button id="import-library-btn" class="btn-secondary">Import</button>
|
|
||||||
<input type="file" id="import-library-input" style="display: none;" accept=".json">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="api-instance-manager">
|
|
||||||
<div class="setting-item" style="padding-bottom: 1rem; border: none;">
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<span class="label">API Instances</span>
|
<span class="label">Theme</span>
|
||||||
<span class="description">Manage and prioritize API instances. Automatically sorted by speed.</span>
|
<span class="description">Choose your preferred color scheme</span>
|
||||||
</div>
|
</div>
|
||||||
<button id="refresh-speed-test-btn" class="btn-secondary">Refresh Speed Test</button>
|
|
||||||
</div>
|
</div>
|
||||||
<ul id="api-instance-list"></ul>
|
<div class="theme-picker" id="theme-picker">
|
||||||
|
<div class="theme-option" data-theme="system">System</div>
|
||||||
|
<div class="theme-option" data-theme="light">Light</div>
|
||||||
|
<div class="theme-option" data-theme="dark">Dark</div>
|
||||||
|
<div class="theme-option" data-theme="monochrome">Black</div>
|
||||||
|
<div class="theme-option" data-theme="ocean">Ocean</div>
|
||||||
|
<div class="theme-option" data-theme="purple">Purple</div>
|
||||||
|
<div class="theme-option" data-theme="forest">Forest</div>
|
||||||
|
<div class="theme-option" data-theme="custom">Custom</div>
|
||||||
|
</div>
|
||||||
|
<div class="custom-theme-editor" id="custom-theme-editor">
|
||||||
|
<h4>Custom Theme</h4>
|
||||||
|
<div class="theme-color-grid" id="theme-color-grid"></div>
|
||||||
|
<div class="theme-actions">
|
||||||
|
<button class="btn-secondary" id="apply-custom-theme">Apply Theme</button>
|
||||||
|
<button class="btn-secondary" id="reset-custom-theme">Reset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Album Cover Background</span>
|
||||||
|
<span class="description">Use the album cover as a blurred background on album pages and as primary color</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="album-background-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Last.fm Scrobbling</span>
|
||||||
|
<span class="description" id="lastfm-status">Connect your Last.fm account to scrobble tracks</span>
|
||||||
|
</div>
|
||||||
|
<div id="lastfm-controls">
|
||||||
|
<button id="lastfm-connect-btn" class="btn-secondary">Connect Last.fm</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item" id="lastfm-toggle-setting" style="display: none;">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Enable Scrobbling</span>
|
||||||
|
<span class="description">Automatically scrobble played tracks</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="lastfm-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Firebase Configuration</span>
|
||||||
|
<span class="description">Manage your database connection.</span>
|
||||||
|
</div>
|
||||||
|
<div class="firebase-settings-wrapper">
|
||||||
|
<button id="toggle-firebase-config-btn" class="btn-secondary">Advanced: Custom Configuration</button>
|
||||||
|
<div id="custom-firebase-config-container" class="custom-firebase-config">
|
||||||
|
<p class="config-help-text">Default shared instance is active. <a href="firebase-setup.md" target="_blank" class="text-link">Override below only if needed.</a></p>
|
||||||
|
<textarea id="firebase-config-input" class="template-input" rows="5" placeholder='{ "apiKey": "...", "authDomain": "...", ... }'></textarea>
|
||||||
|
<div class="firebase-controls-container">
|
||||||
|
<button id="save-firebase-config-btn" class="btn-secondary">Save & Reload</button>
|
||||||
|
<button id="share-firebase-config-btn" class="btn-secondary">Share</button>
|
||||||
|
<button id="clear-firebase-config-btn" class="btn-secondary danger">Clear Config</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Sync & Backup (Beta)</span>
|
||||||
|
<span class="description" id="firebase-status">Sync your library across devices</span>
|
||||||
|
<div style="display: flex; gap: 0.5rem; margin-top: 0.5rem; align-items: center;">
|
||||||
|
<img id="firebase-user-avatar" src="" style="width: 24px; height: 24px; border-radius: 50%; display: none;" onerror="this.style.display='none'" onload="this.style.display='block'">
|
||||||
|
<span id="firebase-user-name" style="font-size: 0.85rem; color: var(--foreground);"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="firebase-controls">
|
||||||
|
<button id="firebase-connect-btn" class="btn-secondary">Connect with Google</button>
|
||||||
|
<button id="firebase-clear-cloud-btn" class="btn-secondary danger" style="display: none; margin-top: 0.5rem;">Clear Cloud Data</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Audio Quality</span>
|
||||||
|
<span class="description">Quality for streaming and downloads</span>
|
||||||
|
</div>
|
||||||
|
<select id="quality-setting">
|
||||||
|
<option value="LOSSLESS">FLAC (Lossless)</option>
|
||||||
|
<option value="HIGH">AAC 320kbps</option>
|
||||||
|
<option value="LOW">AAC 96kbps</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Gapless Playback</span>
|
||||||
|
<span class="description">Play audio without interruption between tracks</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" checked>
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Now Playing View Mode</span>
|
||||||
|
<span class="description">Choose what shows when you click the album art</span>
|
||||||
|
</div>
|
||||||
|
<select id="now-playing-mode">
|
||||||
|
<option value="album">Show Album</option>
|
||||||
|
<option value="cover">Enlarged Cover</option>
|
||||||
|
<option value="lyrics">Lyrics Panel</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Track List Actions</span>
|
||||||
|
<span class="description">Choose between a dropdown menu or inline buttons for track actions</span>
|
||||||
|
</div>
|
||||||
|
<select id="track-list-actions-mode">
|
||||||
|
<option value="dropdown">Dropdown Menu</option>
|
||||||
|
<option value="inline">Inline Buttons</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Download Lyrics</span>
|
||||||
|
<span class="description">Include .lrc files when downloading tracks/albums</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle-switch">
|
||||||
|
<input type="checkbox" id="download-lyrics-toggle">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Filename Template</span>
|
||||||
|
<span class="description">Customize download filenames. Available: {trackNumber}, {artist}, {title}, {album}</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="filename-template" class="template-input" placeholder="{trackNumber} - {artist} - {title}">
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">ZIP Folder Template</span>
|
||||||
|
<span class="description">Customize album folder names. Available: {albumTitle}, {albumArtist}, {year}</span>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="zip-folder-template" class="template-input" placeholder="{albumTitle} - {albumArtist} - monochrome.tf">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="settings-group">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Keyboard Shortcuts</span>
|
||||||
|
<span class="description">View available keyboard shortcuts</span>
|
||||||
|
</div>
|
||||||
|
<button id="show-shortcuts-btn" class="btn-secondary">Show Shortcuts</button>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Cache</span>
|
||||||
|
<span class="description" id="cache-info">Stores API responses to reduce requests</span>
|
||||||
|
</div>
|
||||||
|
<button id="clear-cache-btn" class="btn-secondary">Clear Cache</button>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">Backup & Restore</span>
|
||||||
|
<span class="description">Export or import your library and history as JSON</span>
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; gap: 0.5rem;">
|
||||||
|
<button id="export-library-btn" class="btn-secondary">Export</button>
|
||||||
|
<button id="import-library-btn" class="btn-secondary">Import</button>
|
||||||
|
<input type="file" id="import-library-input" style="display: none;" accept=".json">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="api-instance-manager">
|
||||||
|
<div class="setting-item" style="padding-bottom: 1rem; border: none;">
|
||||||
|
<div class="info">
|
||||||
|
<span class="label">API Instances</span>
|
||||||
|
<span class="description">Manage and prioritize API instances. Automatically sorted by speed.</span>
|
||||||
|
</div>
|
||||||
|
<button id="refresh-speed-test-btn" class="btn-secondary">Refresh Speed Test</button>
|
||||||
|
</div>
|
||||||
|
<ul id="api-instance-list"></ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ export class AuthManager {
|
||||||
|
|
||||||
updateUI(user) {
|
updateUI(user) {
|
||||||
const connectBtn = document.getElementById('firebase-connect-btn');
|
const connectBtn = document.getElementById('firebase-connect-btn');
|
||||||
|
const clearDataBtn = document.getElementById('firebase-clear-cloud-btn');
|
||||||
const statusText = document.getElementById('firebase-status');
|
const statusText = document.getElementById('firebase-status');
|
||||||
const userAvatar = document.getElementById('firebase-user-avatar');
|
const userAvatar = document.getElementById('firebase-user-avatar');
|
||||||
const userName = document.getElementById('firebase-user-name');
|
const userName = document.getElementById('firebase-user-name');
|
||||||
|
|
@ -70,6 +71,8 @@ export class AuthManager {
|
||||||
connectBtn.classList.add('danger');
|
connectBtn.classList.add('danger');
|
||||||
connectBtn.onclick = () => this.signOut();
|
connectBtn.onclick = () => this.signOut();
|
||||||
|
|
||||||
|
if (clearDataBtn) clearDataBtn.style.display = 'block';
|
||||||
|
|
||||||
if (statusText) statusText.textContent = `Signed in as ${user.email}`;
|
if (statusText) statusText.textContent = `Signed in as ${user.email}`;
|
||||||
|
|
||||||
// Optional: Show user info if elements exist
|
// Optional: Show user info if elements exist
|
||||||
|
|
@ -81,6 +84,8 @@ export class AuthManager {
|
||||||
connectBtn.classList.remove('danger');
|
connectBtn.classList.remove('danger');
|
||||||
connectBtn.onclick = () => this.signInWithGoogle();
|
connectBtn.onclick = () => this.signInWithGoogle();
|
||||||
|
|
||||||
|
if (clearDataBtn) clearDataBtn.style.display = 'none';
|
||||||
|
|
||||||
if (statusText) statusText.textContent = 'Sync your library across devices';
|
if (statusText) statusText.textContent = 'Sync your library across devices';
|
||||||
|
|
||||||
if (userAvatar) userAvatar.src = ''; // Placeholder or clear
|
if (userAvatar) userAvatar.src = ''; // Placeholder or clear
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,15 @@ let provider = null;
|
||||||
|
|
||||||
const STORAGE_KEY = 'monochrome-firebase-config';
|
const STORAGE_KEY = 'monochrome-firebase-config';
|
||||||
|
|
||||||
|
const DEFAULT_CONFIG = {
|
||||||
|
apiKey: "AIzaSyDPU-unAjuLtQJt4IkGS5faG50UCF7lYyA",
|
||||||
|
authDomain: "monochrome-database.firebaseapp.com",
|
||||||
|
projectId: "monochrome-database",
|
||||||
|
storageBucket: "monochrome-database.firebasestorage.app",
|
||||||
|
messagingSenderId: "895657412760",
|
||||||
|
appId: "1:895657412760:web:e81c5044c7f4e9b799e8ed"
|
||||||
|
};
|
||||||
|
|
||||||
function getStoredConfig() {
|
function getStoredConfig() {
|
||||||
try {
|
try {
|
||||||
const stored = localStorage.getItem(STORAGE_KEY);
|
const stored = localStorage.getItem(STORAGE_KEY);
|
||||||
|
|
@ -21,19 +30,21 @@ function getStoredConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to initialize on load
|
// Attempt to initialize on load
|
||||||
const config = getStoredConfig();
|
const storedConfig = getStoredConfig();
|
||||||
|
const config = storedConfig || DEFAULT_CONFIG;
|
||||||
|
|
||||||
if (config) {
|
if (config) {
|
||||||
try {
|
try {
|
||||||
app = initializeApp(config);
|
app = initializeApp(config);
|
||||||
auth = getAuth(app);
|
auth = getAuth(app);
|
||||||
database = getDatabase(app);
|
database = getDatabase(app);
|
||||||
provider = new GoogleAuthProvider();
|
provider = new GoogleAuthProvider();
|
||||||
console.log("Firebase initialized from saved config");
|
console.log("Firebase initialized from " + (storedConfig ? "saved" : "default") + " config");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error initializing Firebase from saved config:", error);
|
console.error("Error initializing Firebase:", error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("No Firebase config found in local storage.");
|
console.log("No Firebase config found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveFirebaseConfig(configObj) {
|
export function saveFirebaseConfig(configObj) {
|
||||||
|
|
@ -113,6 +124,22 @@ export function initializeFirebaseSettingsUI() {
|
||||||
const saveFirebaseConfigBtn = document.getElementById('save-firebase-config-btn');
|
const saveFirebaseConfigBtn = document.getElementById('save-firebase-config-btn');
|
||||||
const clearFirebaseConfigBtn = document.getElementById('clear-firebase-config-btn');
|
const clearFirebaseConfigBtn = document.getElementById('clear-firebase-config-btn');
|
||||||
const shareFirebaseConfigBtn = document.getElementById('share-firebase-config-btn');
|
const shareFirebaseConfigBtn = document.getElementById('share-firebase-config-btn');
|
||||||
|
const toggleFirebaseConfigBtn = document.getElementById('toggle-firebase-config-btn');
|
||||||
|
const customFirebaseConfigContainer = document.getElementById('custom-firebase-config-container');
|
||||||
|
|
||||||
|
// Toggle Button Logic
|
||||||
|
if (toggleFirebaseConfigBtn && customFirebaseConfigContainer) {
|
||||||
|
toggleFirebaseConfigBtn.addEventListener('click', () => {
|
||||||
|
const isVisible = customFirebaseConfigContainer.classList.contains('visible');
|
||||||
|
if (isVisible) {
|
||||||
|
customFirebaseConfigContainer.classList.remove('visible');
|
||||||
|
toggleFirebaseConfigBtn.textContent = 'Advanced: Custom Configuration';
|
||||||
|
} else {
|
||||||
|
customFirebaseConfigContainer.classList.add('visible');
|
||||||
|
toggleFirebaseConfigBtn.textContent = 'Hide Custom Configuration';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Populate current config
|
// Populate current config
|
||||||
if (firebaseConfigInput) {
|
if (firebaseConfigInput) {
|
||||||
|
|
@ -120,6 +147,11 @@ export function initializeFirebaseSettingsUI() {
|
||||||
if (currentConfig) {
|
if (currentConfig) {
|
||||||
try {
|
try {
|
||||||
firebaseConfigInput.value = JSON.stringify(JSON.parse(currentConfig), null, 2);
|
firebaseConfigInput.value = JSON.stringify(JSON.parse(currentConfig), null, 2);
|
||||||
|
// If custom config exists, show the container
|
||||||
|
if (customFirebaseConfigContainer && toggleFirebaseConfigBtn) {
|
||||||
|
customFirebaseConfigContainer.classList.add('visible');
|
||||||
|
toggleFirebaseConfigBtn.textContent = 'Hide Custom Configuration';
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
firebaseConfigInput.value = currentConfig;
|
firebaseConfigInput.value = currentConfig;
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +224,7 @@ export function initializeFirebaseSettingsUI() {
|
||||||
// Clear Button
|
// Clear Button
|
||||||
if (clearFirebaseConfigBtn) {
|
if (clearFirebaseConfigBtn) {
|
||||||
clearFirebaseConfigBtn.addEventListener('click', () => {
|
clearFirebaseConfigBtn.addEventListener('click', () => {
|
||||||
if (confirm('Are you sure you want to clear the Firebase configuration? Sync will stop.')) {
|
if (confirm('Are you sure you want to remove the custom configuration? The app will revert to the shared default database.')) {
|
||||||
clearFirebaseConfig();
|
clearFirebaseConfig();
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,16 @@ export class SyncManager {
|
||||||
const itemRef = child(this.userRef, path);
|
const itemRef = child(this.userRef, path);
|
||||||
|
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
await set(itemRef, item);
|
// Minify to ensure consistency and reduce bandwidth
|
||||||
|
// We use the db helper to ensure consistent structure
|
||||||
|
const minified = db._minifyItem(type, item);
|
||||||
|
// Ensure addedAt is present. If the passed item didn't have it (e.g. from player),
|
||||||
|
// we add it now. Ideally this matches local DB time, but a small diff is negligible.
|
||||||
|
const entry = {
|
||||||
|
...minified,
|
||||||
|
addedAt: item.addedAt || minified.addedAt || Date.now()
|
||||||
|
};
|
||||||
|
await set(itemRef, entry);
|
||||||
} else {
|
} else {
|
||||||
await remove(itemRef);
|
await remove(itemRef);
|
||||||
}
|
}
|
||||||
|
|
@ -222,6 +231,13 @@ export class SyncManager {
|
||||||
console.error("Failed to sync history item:", error);
|
console.error("Failed to sync history item:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async clearCloudData() {
|
||||||
|
if (!this.user || !this.userRef) {
|
||||||
|
throw new Error("Not authenticated");
|
||||||
|
}
|
||||||
|
await remove(this.userRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const syncManager = new SyncManager();
|
export const syncManager = new SyncManager();
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,19 @@ export function initializeSettings(scrobbler, player, api, ui) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('firebase-clear-cloud-btn')?.addEventListener('click', async () => {
|
||||||
|
if (confirm('Are you sure you want to delete ALL your data from the cloud? This cannot be undone.')) {
|
||||||
|
try {
|
||||||
|
await syncManager.clearCloudData();
|
||||||
|
alert('Cloud data cleared successfully.');
|
||||||
|
authManager.signOut();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clear cloud data:', error);
|
||||||
|
alert('Failed to clear cloud data: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Backup & Restore
|
// Backup & Restore
|
||||||
document.getElementById('export-library-btn')?.addEventListener('click', async () => {
|
document.getElementById('export-library-btn')?.addEventListener('click', async () => {
|
||||||
const data = await db.exportData();
|
const data = await db.exportData();
|
||||||
|
|
|
||||||
64
styles.css
64
styles.css
|
|
@ -174,6 +174,16 @@ a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-link {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: underline;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-link:hover {
|
||||||
|
color: var(--highlight);
|
||||||
|
}
|
||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
background-color: var(--secondary);
|
background-color: var(--secondary);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
|
@ -997,10 +1007,22 @@ body.has-page-background .track-item:hover {
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-group {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding: var(--spacing-lg) 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-group .setting-item {
|
||||||
|
border-bottom: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.setting-item {
|
.setting-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
|
||||||
padding: var(--spacing-lg) 0;
|
padding: var(--spacing-lg) 0;
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
gap: var(--spacing-lg);
|
gap: var(--spacing-lg);
|
||||||
|
|
@ -3200,4 +3222,44 @@ img:not([src]), img[src=''] {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Firebase Settings Styling */
|
||||||
|
.firebase-settings-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-firebase-config {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-firebase-config.visible {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-help-text {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#firebase-controls {
|
||||||
|
text-align: end;
|
||||||
|
}
|
||||||
|
.firebase-controls-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-firebase-config-btn {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue