Refresh the page automatically after a pending offline sync completes

The real dashboard is server-rendered before any client JS runs, so
the first page load after reconnecting always reflects the pre-sync
database state -- the background sync then completes silently, but
nothing told that already-rendered page to update. A task created
offline would appear to vanish until the next navigation happened to
load fresh data. runBackgroundSync() now reports whether it pushed
pending changes, and both places that trigger it automatically reload
the page when it did.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2026-09-05 00:05:27 -06:00
co-authored by Claude Sonnet 5
parent a6f227e931
commit ae9ab596bb
3 changed files with 21 additions and 8 deletions
+5 -2
View File
@@ -11,8 +11,11 @@ document.addEventListener('DOMContentLoaded', function() {
maybeBackgroundSync(); maybeBackgroundSync();
}); });
window.addEventListener('online', function() { window.addEventListener('online', async function() {
runBackgroundSync(); const pushedChanges = await runBackgroundSync();
if (pushedChanges) {
window.location.reload();
}
}); });
/* ============================================ /* ============================================
+14 -4
View File
@@ -92,10 +92,14 @@ async function mergeAndResolveEntity({ entityType, dirtyLocal, serverRows, confl
} }
} }
// Returns true if locally-queued offline changes were successfully pushed up
// this call - meaning the current (server-rendered) page was rendered before
// those changes existed and is now stale, so the caller should refresh it.
// Returns false if there was nothing to push, or the sync didn't complete.
async function runBackgroundSync() { async function runBackgroundSync() {
const csrfToken = getCsrfToken(); const csrfToken = getCsrfToken();
if (!csrfToken) { if (!csrfToken) {
return; // Not on an authenticated page (e.g. login/register). return false; // Not on an authenticated page (e.g. login/register).
} }
const deviceId = await ensureDeviceId(); const deviceId = await ensureDeviceId();
@@ -104,6 +108,7 @@ async function runBackgroundSync() {
const dirtyTasks = await getDirtyTasks(); const dirtyTasks = await getDirtyTasks();
const dirtyTags = await getDirtyTags(); const dirtyTags = await getDirtyTags();
const dirtyTimeEntries = await getDirtyTimeEntries(); const dirtyTimeEntries = await getDirtyTimeEntries();
const hadPendingChanges = dirtyTasks.length + dirtyTags.length + dirtyTimeEntries.length > 0;
let response; let response;
try { try {
@@ -124,11 +129,11 @@ async function runBackgroundSync() {
}), }),
}); });
} catch (err) { } catch (err) {
return; // Offline or network error - retry next time, nothing to clean up. return false; // Offline or network error - retry next time, nothing to clean up.
} }
if (!response.ok) { if (!response.ok) {
return; // Includes 429 throttled - retry next time. return false; // Includes 429 throttled - retry next time.
} }
const data = await response.json(); const data = await response.json();
@@ -149,6 +154,8 @@ async function runBackgroundSync() {
await setMeta('last_sync_token', data.sync_token); await setMeta('last_sync_token', data.sync_token);
await setMeta('last_sync_at', new Date().toISOString()); await setMeta('last_sync_at', new Date().toISOString());
return hadPendingChanges;
} }
async function maybeBackgroundSync() { async function maybeBackgroundSync() {
@@ -156,5 +163,8 @@ async function maybeBackgroundSync() {
if (lastSyncAt && Date.now() - new Date(lastSyncAt).getTime() < BACKGROUND_SYNC_MIN_INTERVAL_MS) { if (lastSyncAt && Date.now() - new Date(lastSyncAt).getTime() < BACKGROUND_SYNC_MIN_INTERVAL_MS) {
return; return;
} }
runBackgroundSync(); const pushedChanges = await runBackgroundSync();
if (pushedChanges) {
window.location.reload();
}
} }
+2 -2
View File
@@ -148,8 +148,8 @@
{% endblock %} {% endblock %}
<script src="{% static 'js/offline-db.js' %}?v=7"></script> <script src="{% static 'js/offline-db.js' %}?v=7"></script>
<script src="{% static 'js/offline-sync.js' %}?v=7"></script> <script src="{% static 'js/offline-sync.js' %}?v=8"></script>
<script src="{% static 'js/app.js' %}?v=7"></script> <script src="{% static 'js/app.js' %}?v=8"></script>
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
</body> </body>
</html> </html>