diff --git a/_locales/en/messages.json b/_locales/en/messages.json index db137368..c9fb1aa6 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1530,6 +1530,10 @@ "message": "The value cannot be saved. Try reducing the amount of text.", "description": "Displayed when trying to save an excessively big value via storage.sync API" }, + "syncError": { + "message": "Sync failed", + "description": "Tooltip for the toolbar icon" + }, "syncErrorRelogin": { "message": "Sync failed.\nTry to re-login in Stylus options:\nclick 'disconnect' first, then 'connect'.", "description": "Tooltip for the toolbar icon" diff --git a/background/sync-manager.js b/background/sync-manager.js index c680898d..db816a1e 100644 --- a/background/sync-manager.js +++ b/background/sync-manager.js @@ -27,6 +27,7 @@ const syncMan = (() => { errorMessage: null, login: false, }; + let lastError = null; let ctrl; let currentDrive; /** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */ @@ -100,8 +101,10 @@ const syncMan = (() => { } await syncMan.syncNow(); status.errorMessage = null; + lastError = null; } catch (err) { status.errorMessage = err.message; + lastError = err; // FIXME: should we move this logic to options.js? if (!fromPref) { console.error(err); @@ -139,8 +142,10 @@ const syncMan = (() => { try { await (ctrl.isInit() ? ctrl.syncNow() : ctrl.start()).catch(handle401Error); status.errorMessage = null; + lastError = null; } catch (err) { status.errorMessage = err.message; + lastError = err; } emitStatusChange(); }, @@ -188,27 +193,48 @@ const syncMan = (() => { } async function handle401Error(err) { - let emit; + let authError = false; if (err.code === 401) { await tokenMan.revokeToken(currentDrive.name).catch(console.error); - emit = true; + authError = true; } else if (/User interaction required|Requires user interaction/i.test(err.message)) { - emit = true; + authError = true; } - if (emit) { + if (authError) { status.login = false; emitStatusChange(); } - iconMan.overrideBadge(!emit ? {} : { - text: 'x', - color: '#F00', - title: chrome.i18n.getMessage('syncErrorRelogin'), - }); return Promise.reject(err); } function emitStatusChange() { msg.broadcastExtension({method: 'syncStatusUpdate', status}); + + if (status.state !== STATES.connected || !lastError || isNetworkError(lastError)) { + iconMan.overrideBadge({}); + } else if (isGrantError(lastError)) { + iconMan.overrideBadge({ + text: 'x', + color: '#F00', + title: chrome.i18n.getMessage('syncErrorRelogin'), + }); + } else { + iconMan.overrideBadge({ + text: 'x', + color: '#F00', + title: chrome.i18n.getMessage('syncError'), + }); + } + } + + function isNetworkError(err) { + return err.name === 'TypeError' && /networkerror|failed to fetch/i.test(err.message); + } + + function isGrantError(err) { + if (err.code === 401) return true; + if (err.code === 400 && /invalid_grant/.test(err.message)) return true; + return false; } function getDrive(name) { diff --git a/package-lock.json b/package-lock.json index 6abf6ec6..9fce00ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -645,7 +645,6 @@ "espree": "7.3.0", "esprima": "4.0.1", "fluent-syntax": "0.13.0", - "fsevents": "2.2.1", "glob": "7.1.6", "is-mergeable-object": "1.1.1", "jed": "1.1.1", @@ -1855,12 +1854,6 @@ "engines": [ "node >=0.10.0" ], - "dependencies": { - "dtrace-provider": "~0.8", - "moment": "^2.19.3", - "mv": "~2", - "safe-json-stringify": "~1" - }, "bin": { "bunyan": "bin/bunyan" }, @@ -2144,7 +2137,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.1.2", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -4559,9 +4551,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -6995,7 +6984,6 @@ "integrity": "sha512-ssHt0dkljEDaKmTgQ04DQgx2ag6G2gMPxA5hpcsoeTbfDgRf2fC2gNSRc6kISjD7ckCpHwwQvXxuTBK8402fXg==", "dev": true, "dependencies": { - "encoding": "^0.1.12", "minipass": "^3.1.0", "minipass-pipeline": "^1.2.2", "minipass-sized": "^1.0.3", @@ -11284,10 +11272,8 @@ "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==", "dev": true, "dependencies": { - "chokidar": "^3.4.1", "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0", - "watchpack-chokidar2": "^2.0.0" + "neo-async": "^2.5.0" }, "optionalDependencies": { "chokidar": "^3.4.1", @@ -11371,7 +11357,6 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0",