Skip to content

Commit 28a6635

Browse files
nichu42Copilot
andcommitted
Preserve settings across upgrades
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 66a4086 commit 28a6635

5 files changed

Lines changed: 138 additions & 4 deletions

File tree

manifest.firefox.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 2,
33
"name": "Haiilo Enhancer",
4-
"version": "0.4.0",
4+
"version": "0.4.1",
55
"description": "Enhance your Haiilo experience - mute users, customize your feed, and more",
66
"homepage_url": "https://github.com/nichu42/haiilo-enhancer",
77
"permissions": [

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "Haiilo Enhancer",
4-
"version": "0.4.0",
4+
"version": "0.4.1",
55
"description": "Enhance your Haiilo experience - mute users, customize your feed, and more",
66
"homepage_url": "https://github.com/nichu42/haiilo-enhancer",
77
"permissions": [

options.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,38 @@ footer .donate-icon {
385385
border-radius: 4px;
386386
}
387387

388+
.reauth-warning {
389+
margin-top: 16px;
390+
padding: 14px 16px;
391+
background: var(--color-warning-bg);
392+
border: 1px solid var(--color-warning-border);
393+
border-left: 4px solid var(--color-warning);
394+
border-radius: 8px;
395+
color: var(--color-text-primary);
396+
font-size: 13px;
397+
line-height: 1.5;
398+
}
399+
400+
.reauth-warning strong {
401+
display: block;
402+
margin-bottom: 6px;
403+
color: var(--color-warning);
404+
}
405+
406+
.reauth-warning p {
407+
margin: 6px 0;
408+
}
409+
410+
.reauth-warning ul {
411+
margin: 6px 0 10px 20px;
412+
padding: 0;
413+
}
414+
415+
.reauth-warning li {
416+
font-family: monospace;
417+
font-size: 13px;
418+
}
419+
388420
.domains-list {
389421
margin-bottom: 16px;
390422
}

options.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,30 @@ <h2>Custom Haiilo Domains</h2>
252252

253253
<section>
254254
<h2>Data Management</h2>
255+
<div class="form-group checkbox-group">
256+
<label>
257+
<input type="checkbox" id="cloudSync">
258+
<span>Sync settings and muted users via browser account</span>
259+
</label>
260+
<small class="help-text">When enabled, your settings and up to 50 muted users are stored in your browser's built-in sync storage (Google account for Chrome, Firefox account for Firefox) and roam across devices. Custom domains are excluded — re-add them manually on each device. Cloud sync is disabled automatically if your muted list exceeds 50 users.</small>
261+
<div id="cloudSyncWarning" class="reauth-warning" style="display: none;">
262+
<strong>⚠️ Cloud sync was automatically disabled</strong>
263+
<p>Your muted users list exceeded the 50-user limit for cloud sync. Your data is still stored locally and nothing was lost. To re-enable cloud sync, reduce your muted users list to 50 or fewer.</p>
264+
</div>
265+
</div>
255266
<p class="description">Export your settings and muted users to a JSON file as a backup, or import a previous export to restore them. <strong>Reset to Defaults</strong> wipes everything; export first if you might want to undo it.</p>
256267
<div class="button-group">
257268
<button id="exportData" class="secondary">Export All Settings</button>
258269
<button id="importData" class="secondary">Import All Settings</button>
259270
<button id="clearAll" class="danger">Reset Everything to Defaults</button>
260271
</div>
261272
<input type="file" id="importFile" accept=".json" style="display: none;">
273+
<div id="domainReauthWarning" class="reauth-warning" style="display: none;">
274+
<strong>⚠️ Custom domains need permission re-grant</strong>
275+
<p>The following domains were in your backup but could not be restored automatically — browser security requires you to grant access manually:</p>
276+
<ul id="domainReauthList"></ul>
277+
<p>To re-add them: scroll down to <strong>Custom Haiilo Domains</strong>, enter each domain, and click <strong>Add Domain</strong> to grant browser access.</p>
278+
</div>
262279
</section>
263280

264281
<div class="save-status" id="saveStatus"></div>

options.js

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ function debugLog(...args) {
5959
});
6060
}
6161

62+
function updateCloudSyncWarning(settings) {
63+
const warning = document.getElementById('cloudSyncWarning');
64+
if (!warning) return;
65+
// Show the warning if cloudSync was disabled externally (i.e. user limit exceeded)
66+
// We detect this by checking if the checkbox is unchecked but the stored value was true
67+
// The background sets cloudSync = false when limit exceeded and broadcasts settingsUpdated.
68+
// The warning is shown only when explicitly triggered via cloudSyncDisabled message.
69+
warning.style.display = 'none';
70+
}
71+
72+
// Listen for cloudSyncDisabled broadcast from background
73+
browserAPI.runtime.onMessage.addListener((message) => {
74+
if (message.action === 'cloudSyncDisabled') {
75+
const warning = document.getElementById('cloudSyncWarning');
76+
if (warning) warning.style.display = 'block';
77+
const cloudSyncCheckbox = document.getElementById('cloudSync');
78+
if (cloudSyncCheckbox) cloudSyncCheckbox.checked = false;
79+
}
80+
});
81+
6282
if (document.readyState === 'loading') {
6383
document.addEventListener('DOMContentLoaded', initOptions);
6484
} else {
@@ -127,6 +147,19 @@ async function loadSettings() {
127147
const scope = settings.autoExpandScope;
128148
document.getElementById('autoExpandScope').value = (scope === 'workspaces' || scope === 'pages') ? scope : 'both';
129149

150+
// Cloud sync
151+
const cloudSyncCheckbox = document.getElementById('cloudSync');
152+
if (cloudSyncCheckbox) {
153+
cloudSyncCheckbox.checked = settings.cloudSync === true;
154+
// Show warning if sync was previously enabled but muted list now exceeds limit
155+
const mutedUsers = await browserAPI.runtime.sendMessage({ action: 'getMutedUsers' });
156+
const warning = document.getElementById('cloudSyncWarning');
157+
if (warning && !settings.cloudSync && Array.isArray(mutedUsers) && mutedUsers.length > 50) {
158+
warning.style.display = 'block';
159+
}
160+
updateCloudSyncWarning(settings);
161+
}
162+
130163
// Show/hide channel avatar settings based on checkbox
131164
toggleChannelAvatarSettings();
132165
toggleStyleSettings();
@@ -334,6 +367,26 @@ function setupEventListeners() {
334367
// Auto-expand settings
335368
document.getElementById('autoExpandEnabled').addEventListener('change', saveSettings);
336369
document.getElementById('autoExpandScope').addEventListener('change', saveSettings);
370+
371+
// Cloud sync toggle
372+
const cloudSyncCheckbox = document.getElementById('cloudSync');
373+
if (cloudSyncCheckbox) {
374+
cloudSyncCheckbox.addEventListener('change', async (e) => {
375+
if (e.target.checked) {
376+
// Test whether storage.sync is actually available before enabling
377+
try {
378+
await browserAPI.storage.sync.set({ __haiiloSyncTest: true });
379+
await browserAPI.storage.sync.remove('__haiiloSyncTest');
380+
} catch (err) {
381+
// Sync not available — revert the checkbox and warn the user
382+
e.target.checked = false;
383+
showStatus('Cloud sync is not available. Make sure you are signed into your browser account.', 'error');
384+
return;
385+
}
386+
}
387+
saveSettings();
388+
});
389+
}
337390
document.getElementById('autoExpandClicksPerList').addEventListener('change', () => {
338391
// Clamp the value client-side as a safety net.
339392
const input = document.getElementById('autoExpandClicksPerList');
@@ -545,7 +598,8 @@ async function saveSettings() {
545598
autoExpandEnabled: document.getElementById('autoExpandEnabled').checked,
546599
autoExpandClicksPerList: parseInt(document.getElementById('autoExpandClicksPerList').value, 10) || 3,
547600
autoExpandDelayMs: parseInt(document.getElementById('autoExpandDelayMs').value, 10) || 300,
548-
autoExpandScope: document.getElementById('autoExpandScope').value
601+
autoExpandScope: document.getElementById('autoExpandScope').value,
602+
cloudSync: document.getElementById('cloudSync') ? document.getElementById('cloudSync').checked : false
549603
};
550604

551605
await browserAPI.runtime.sendMessage({ action: 'saveSettings', settings });
@@ -585,12 +639,16 @@ async function applyLocaleDefaults() {
585639
async function exportData() {
586640
const mutedUsers = await browserAPI.runtime.sendMessage({ action: 'getMutedUsers' });
587641
const settings = await browserAPI.runtime.sendMessage({ action: 'getSettings' });
642+
const customDomains = await browserAPI.runtime.sendMessage({ action: 'getCustomDomains' });
643+
const customHomepages = await browserAPI.runtime.sendMessage({ action: 'getCustomHomepages' });
588644

589645
const exportObj = {
590646
version: '1.0',
591647
exportedAt: new Date().toISOString(),
592648
mutedUsers,
593-
settings
649+
settings,
650+
customDomains: customDomains || [],
651+
customHomepages: customHomepages || {}
594652
};
595653

596654
const blob = new Blob([JSON.stringify(exportObj, null, 2)], { type: 'application/json' });
@@ -637,9 +695,36 @@ async function importData(e) {
637695
await loadSettings();
638696
}
639697

698+
// Custom domains are intentionally NOT restored — browser host permissions
699+
// require a user gesture (click) and cannot be granted silently during import.
700+
// Show a warning banner listing the domains the user must re-add manually.
701+
const domainReauthWarning = document.getElementById('domainReauthWarning');
702+
const domainReauthList = document.getElementById('domainReauthList');
703+
if (data.customDomains && Array.isArray(data.customDomains) && data.customDomains.length > 0 && domainReauthWarning && domainReauthList) {
704+
domainReauthList.textContent = '';
705+
data.customDomains.forEach(domain => {
706+
const li = document.createElement('li');
707+
li.textContent = domain;
708+
domainReauthList.appendChild(li);
709+
});
710+
domainReauthWarning.style.display = 'block';
711+
} else if (domainReauthWarning) {
712+
domainReauthWarning.style.display = 'none';
713+
}
714+
715+
// Import custom homepages if present
716+
if (data.customHomepages && typeof data.customHomepages === 'object') {
717+
for (const [baseUrl, homepageUrl] of Object.entries(data.customHomepages)) {
718+
await browserAPI.runtime.sendMessage({ action: 'setCustomHomepage', baseUrl, homepageUrl });
719+
}
720+
await loadCustomHomepages();
721+
}
722+
640723
const messages = [];
641724
if (userCount > 0) messages.push(`${userCount} muted users`);
642725
if (data.settings) messages.push('all settings');
726+
if (data.customDomains && data.customDomains.length > 0) messages.push(`${data.customDomains.length} custom domain(s) need manual re-authorization (see warning above)`);
727+
if (data.customHomepages && Object.keys(data.customHomepages).length > 0) messages.push(`${Object.keys(data.customHomepages).length} custom homepage(s)`);
643728

644729
showStatus(`Imported ${messages.join(' and ')}`, 'success');
645730
} catch (err) {

0 commit comments

Comments
 (0)