diff --git a/src/app/browser/preferences/index.tsx b/src/app/browser/preferences/index.tsx
index 3777e31..b84e2be 100644
--- a/src/app/browser/preferences/index.tsx
+++ b/src/app/browser/preferences/index.tsx
@@ -30,6 +30,8 @@ function _Preferences(props: {
const [login_item, ,, forceRefreshLoginItem] = useAsync(useCallback(() => ipc.getLoginItemSettings(), [ipc]));
+ const [show_error_alerts, , show_error_alerts_state, forceRefreshErrorAlerts] = useAsync(useCallback(() => ipc.getShowErrorAlerts(), [ipc]));
+
const setOpenAtLogin = useCallback(async (open_at_login: boolean | 'mixed') => {
await ipc.setLoginItemSettings({...login_item!, startup_enabled: !!open_at_login});
forceRefreshLoginItem();
@@ -39,6 +41,11 @@ function _Preferences(props: {
forceRefreshLoginItem();
}, [ipc, login_item]);
+ const setShowErrorAlerts = useCallback(async (show_error_alerts: boolean | 'mixed') => {
+ await ipc.setShowErrorAlerts(!!show_error_alerts);
+ forceRefreshErrorAlerts();
+ }, [ipc]);
+
const [discord_users, discord_users_error, discord_users_state, forceRefreshDiscordUsers] =
useAsync(useCallback(() => ipc.getDiscordUsers(), [ipc]));
@@ -97,7 +104,8 @@ function _Preferences(props: {
useEventListener(events, 'window:refresh', () => (
forceRefreshAccounts(), forceRefreshLoginItem(),
- forceRefreshDiscordUsers(), forceRefreshDiscordOptions()
+ forceRefreshDiscordUsers(), forceRefreshDiscordOptions(),
+ forceRefreshErrorAlerts()
), []);
if (!users ||
@@ -280,6 +288,26 @@ function _Preferences(props: {
{t('splatnet3.discord_help_2')}
+
+
+
+ {t('miscellaneous.heading')}
+
+
+
+
+ setShowErrorAlerts(!(show_error_alerts ?? true))}>
+ {t('miscellaneous.show_error_alerts')}
+
+
+ {t('miscellaneous.show_error_alerts_help')}
+
+
;
}
diff --git a/src/app/i18n/locale/en-gb.ts b/src/app/i18n/locale/en-gb.ts
index 4492354..6fbef42 100644
--- a/src/app/i18n/locale/en-gb.ts
+++ b/src/app/i18n/locale/en-gb.ts
@@ -292,6 +292,12 @@ export const preferences_window = {
discord_help_1: 'Uses SplatNet 3 to retrieve additional presence information while playing Splatoon 3. You must be using a secondary Nintendo Account that is friends with your main account to fetch your presence, and the secondary account must be able to access SplatNet 3.',
discord_help_2: 'When using a presence URL that returns Splatoon 3 data additional presence information will be shown regardless of this setting.',
},
+
+ miscellaneous: {
+ heading: 'Miscellaneous',
+ show_error_alerts: 'Show error alerts',
+ show_error_alerts_help: 'Shows an alert when an error occurs while updating presence. If this is disabled, nxapi will delay updates after errors.',
+ },
};
export const friend_window = {
diff --git a/src/app/main/ipc.ts b/src/app/main/ipc.ts
index 44b7320..9713b11 100644
--- a/src/app/main/ipc.ts
+++ b/src/app/main/ipc.ts
@@ -90,6 +90,9 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) {
handle('systemPreferences:getloginitem', () => appinstance.store.getLoginItem());
handle('systemPreferences:setloginitem', (e, settings: LoginItemOptions) => appinstance.store.setLoginItem(settings));
+ handle('preferences:getshowerroralerts', () => storage.getItem('ShowErrorAlertsPreference').then(s => s ?? false));
+ handle('preferences:setshowerroralerts', (e, show: boolean) => storage.setItem('ShowErrorAlertsPreference', show));
+
handle('update:get', () => appinstance.updater.cache ?? appinstance.updater.check());
handle('update:check', () => appinstance.updater.check());
handle('statusupdates:get', () => appinstance.statusupdates.cache ?? []);
diff --git a/src/app/main/monitor.ts b/src/app/main/monitor.ts
index 75ead87..5b47712 100644
--- a/src/app/main/monitor.ts
+++ b/src/app/main/monitor.ts
@@ -69,6 +69,9 @@ export class PresenceMonitorManager {
defaultId: 0,
});
+ const show_error_alerts: boolean = await this.app.store.storage.getItem('ShowErrorAlertsPreference') ?? false;
+ if (!show_error_alerts) return ErrorResult.DEFER;
+
if (response === 1) {
return ErrorResult.RETRY;
}
@@ -396,6 +399,9 @@ export class PresenceMonitorManager {
return LoopResult.OK;
}
+ const show_error_alerts: boolean = await this.app.store.storage.getItem('ShowErrorAlertsPreference') ?? false;
+ if (!show_error_alerts) return LoopResult.DEFER_NEXT_UPDATE;
+
const {response} = await showErrorDialog({
message: err.name + ' updating presence monitor',
error: err,
diff --git a/src/app/preload/index.ts b/src/app/preload/index.ts
index f94520e..472332b 100644
--- a/src/app/preload/index.ts
+++ b/src/app/preload/index.ts
@@ -50,6 +50,9 @@ const ipc = {
getLoginItemSettings: () => inv('systemPreferences:getloginitem'),
setLoginItemSettings: (settings: LoginItemOptions) => inv('systemPreferences:setloginitem', settings),
+ getShowErrorAlerts: () => inv('preferences:getshowerroralerts'),
+ setShowErrorAlerts: (show: boolean) => inv('preferences:setshowerroralerts', show),
+
getUpdateData: () => inv('update:get'),
checkUpdates: () => inv('update:check'),
getStatusUpdateData: () => inv('statusupdates:get'),
diff --git a/src/discord/monitor/splatoon3.ts b/src/discord/monitor/splatoon3.ts
index e80d3bd..83d1261 100644
--- a/src/discord/monitor/splatoon3.ts
+++ b/src/discord/monitor/splatoon3.ts
@@ -91,6 +91,7 @@ export default class SplatNet3Monitor extends EmbeddedLoop {
debug('Error authenticating to SplatNet 3', err);
const result = await this.discord_presence.handleError(err as Error);
if (result === ErrorResult.RETRY) return this.init();
+ if (result === ErrorResult.DEFER) return this.errors++, LoopResult.DEFER_NEXT_UPDATE;
if (result === ErrorResult.STOP) return LoopResult.STOP;
}
@@ -168,6 +169,7 @@ export default class SplatNet3Monitor extends EmbeddedLoop {
async handleError(err: Error) {
const result = await this.discord_presence.handleError(err as Error);
if (result === ErrorResult.RETRY) return LoopResult.OK_SKIP_INTERVAL;
+ if (result === ErrorResult.DEFER) return LoopResult.DEFER_NEXT_UPDATE;
this.friend = null;
this.discord_presence.refreshPresence();
diff --git a/src/discord/types.ts b/src/discord/types.ts
index 787460b..86f8a1d 100644
--- a/src/discord/types.ts
+++ b/src/discord/types.ts
@@ -148,5 +148,6 @@ export interface ExternalMonitor extends EmbeddedLoop {
export enum ErrorResult {
STOP,
RETRY,
+ DEFER,
IGNORE,
}
diff --git a/src/util/loop.ts b/src/util/loop.ts
index 6084488..0d4cdfd 100644
--- a/src/util/loop.ts
+++ b/src/util/loop.ts
@@ -4,6 +4,7 @@ const debug = createDebug('nxapi:util:loop');
export default abstract class Loop {
update_interval = 60;
+ errors = 0;
init(): void | Promise {}
@@ -13,8 +14,11 @@ export default abstract class Loop {
try {
const result = init ? await this.init() : await this.update();
+ this.errors = 0;
+
return result ?? (init ? LoopResult.OK_SKIP_INTERVAL : LoopResult.OK);
} catch (err) {
+ this.errors++;
return this.handleError(err as any);
}
}
@@ -23,6 +27,10 @@ export default abstract class Loop {
throw err;
}
+ get next_update_interval() {
+ return this.update_interval * Math.min(this.errors / 2, 20);
+ }
+
private is_loop_active = 0;
async loop(init = false) {
@@ -38,6 +46,14 @@ export default abstract class Loop {
await new Promise(rs => setTimeout(this.timeout_resolve = rs, this.update_interval * 1000));
}
}
+ if (result === LoopResult.DEFER_NEXT_UPDATE) {
+ if (this.skip_interval_once) {
+ this.skip_interval_once = false;
+ } else {
+ await new Promise(rs => setTimeout(this.timeout_resolve = rs, this.next_update_interval * 1000));
+ }
+ return LoopResult.DEFER_NEXT_UPDATE;
+ }
if (result === LoopResult.STOP) {
return LoopResult.STOP;
}
@@ -62,11 +78,13 @@ export default abstract class Loop {
const LoopRunOk = Symbol('LoopRunOk');
const LoopRunOkSkipInterval = Symbol('LoopRunOkSkipInterval');
+const LoopRunIncrementInterval = Symbol('LoopRunIncrementInterval');
const LoopRunStop = Symbol('LoopRunStopNow');
export enum LoopResult {
OK = LoopRunOk as any,
OK_SKIP_INTERVAL = LoopRunOkSkipInterval as any,
+ DEFER_NEXT_UPDATE = LoopRunIncrementInterval as any,
STOP = LoopRunStop as any,
}
@@ -92,25 +110,26 @@ export abstract class EmbeddedLoop extends Loop {
private async _run() {
this._running++;
const i = this._running;
+ let init = true;
try {
- const result = await this.loop(true);
- if (result === LoopResult.STOP) return;
-
while (i === this._running) {
- const result = await this.loop();
+ const result = await this.loop(init);
+
+ if (init && result !== LoopResult.DEFER_NEXT_UPDATE) {
+ init = false;
+ }
if (result === LoopResult.STOP) {
- await this.onStop?.();
+ if (!init) await this.onStop?.();
return;
}
}
- if (this._running === 0 && !this.onStop) {
+ if (this._running === 0 && !init && !this.onStop) {
// Run one more time after the loop ends
const result = await this.loopRun();
}
-
} finally {
this._running = 0;
}