diff --git a/app/static/js/sync.js b/app/static/js/sync.js index fd679b9..44c267f 100644 --- a/app/static/js/sync.js +++ b/app/static/js/sync.js @@ -10,8 +10,7 @@ const Sync = { MAX_RETRIES: 15, VERIFY_DELAY_MS: 800, UPLOAD_TIMEOUT_MS: 120000, - // Backoff delays in ms per retry attempt - BACKOFF_DELAYS: [0, 2000, 5000, 10000, 15000, 30000, 30000, 60000, 60000, 60000, 60000, 60000, 60000, 60000, 60000], + _retryTimerId: null, init(username) { this.currentUsername = username; @@ -34,6 +33,10 @@ const Sync = { console.log('[SYNC] Network offline'); this.isOnline = false; this.isSyncing = false; + if (this._retryTimerId) { + clearTimeout(this._retryTimerId); + this._retryTimerId = null; + } }); }, @@ -63,14 +66,26 @@ const Sync = { console.log('[SYNC] Starting sync...'); this.isSyncing = true; + let hadFailures = false; try { - await this.processQueue(); + hadFailures = await this.processQueue(); } catch (error) { console.error('[SYNC] Error:', error); + hadFailures = true; } finally { this.isSyncing = false; this._setUploading(false); } + + // If there were failures, schedule a retry cycle after a delay + // so it doesn't block the current page load + if (hadFailures && this.isOnline) { + console.log('[SYNC] Scheduling retry in 15s...'); + this._retryTimerId = setTimeout(() => { + this._retryTimerId = null; + this.triggerSync(); + }, 15000); + } }, async processQueue() { @@ -82,9 +97,11 @@ const Sync = { console.log('[SYNC] Found', pendingPhotos.length, 'photos to process'); if (pendingPhotos.length === 0) { - return; + return false; } + let hadFailures = false; + for (const photo of pendingPhotos) { if (!this.isOnline) { console.log('[SYNC] Lost connection, stopping'); @@ -99,21 +116,13 @@ const Sync = { continue; } - // Backoff delay before retry attempts - if (retryCount > 0) { - const backoff = this.BACKOFF_DELAYS[Math.min(retryCount, this.BACKOFF_DELAYS.length - 1)]; - console.log('[SYNC] Waiting', backoff + 'ms before retry', retryCount + 1); - await this.delay(backoff); - - // Re-check connectivity after waiting - if (!this.isOnline) { - console.log('[SYNC] Lost connection during backoff, stopping'); - break; - } - } - - await this.uploadPhoto(photo); + // No delay — upload immediately. Retries are handled by + // scheduling a new triggerSync() after the full queue pass. + const success = await this.uploadPhoto(photo); + if (!success) hadFailures = true; } + + return hadFailures; }, async uploadPhoto(photo) { @@ -134,7 +143,8 @@ const Sync = { // Prevent page navigation during upload this._setUploading(true); - // Check for duplicates on retries (skip on first attempt to reduce latency) + // On retries, first check if a previous attempt actually succeeded + // (the upload may have completed but the verify/status update was interrupted) if (retryCount > 0) { const fullPath = photo.targetPath.replace(/\/$/, '') + '/' + photo.filename; const alreadyExists = await this.checkFileExists(fullPath); @@ -148,7 +158,7 @@ const Sync = { }); await this.pruneHistory(); this._setUploading(false); - return; + return true; } } @@ -227,6 +237,7 @@ const Sync = { // Clear navigation guard after successful upload this._setUploading(false); + return true; } catch (error) { this._setUploading(false); @@ -237,6 +248,7 @@ const Sync = { lastError: error.message, error: error.message }); + return false; } }, @@ -264,7 +276,6 @@ const Sync = { const result = await response.json(); return result.exists === true; } catch (e) { - // If we can't check, assume it doesn't exist and proceed with upload return false; } },