mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-04-26 00:13:08 -05:00
Show presence update errors in the main window
This commit is contained in:
parent
7b838e9b3e
commit
dfb2a3eea7
|
|
@ -1,23 +1,39 @@
|
||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
import { User } from 'discord-rpc';
|
import { User } from 'discord-rpc';
|
||||||
import ipc, { events } from '../ipc.js';
|
import ipc, { events } from '../ipc.js';
|
||||||
import { RequestState, useAsync, useEventListener } from '../util.js';
|
import { RequestState, useAsync, useEventListener } from '../util.js';
|
||||||
import { DiscordPresenceSource, DiscordPresenceSourceUrl, DiscordPresenceSourceCoral } from '../../common/types.js';
|
import { DiscordPresenceSource, DiscordPresenceSourceUrl, DiscordPresenceSourceCoral, DiscordStatus } from '../../common/types.js';
|
||||||
import { DiscordPresence } from '../../../discord/types.js';
|
import { DiscordPresence } from '../../../discord/types.js';
|
||||||
import { DISCORD_COLOUR, TEXT_COLOUR_DARK } from '../constants.js';
|
import { DISCORD_COLOUR, TEXT_COLOUR_DARK } from '../constants.js';
|
||||||
|
import Warning from '../components/icons/warning.js';
|
||||||
|
|
||||||
export default function DiscordPresenceSource(props: {
|
export default function DiscordPresenceSource(props: {
|
||||||
source: DiscordPresenceSource | null;
|
source: DiscordPresenceSource | null;
|
||||||
presence: DiscordPresence | null;
|
presence: DiscordPresence | null;
|
||||||
user: User | null;
|
user: User | null;
|
||||||
}) {
|
}) {
|
||||||
|
const [status, setStatus] = useState<DiscordStatus | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
ipc.getDiscordStatus().then(setStatus);
|
||||||
|
}, [ipc]);
|
||||||
|
|
||||||
|
useEventListener(events, 'update-discord-status', setStatus, []);
|
||||||
|
|
||||||
|
const showErrorDetails = useCallback(() => {
|
||||||
|
ipc.showDiscordLastUpdateError();
|
||||||
|
}, [ipc]);
|
||||||
|
|
||||||
if (!props.source) return null;
|
if (!props.source) return null;
|
||||||
|
|
||||||
return <TouchableOpacity onPress={() => ipc.showDiscordModal()}>
|
return <TouchableOpacity onPress={() => ipc.showDiscordModal()}>
|
||||||
<View style={[styles.discord, !props.source ? styles.discordInactive : null]}>
|
<View style={[styles.discord, !props.source ? styles.discordInactive : null]}>
|
||||||
{renderDiscordPresenceSource(props.source)}
|
{renderDiscordPresenceSource(props.source)}
|
||||||
{props.presence || props.user ? <DiscordPresence presence={props.presence} user={props.user} /> : null}
|
{props.presence || props.user ? <DiscordPresence presence={props.presence} user={props.user} /> : null}
|
||||||
|
|
||||||
|
{status?.error_message ?
|
||||||
|
<DiscordPresenceError message={status?.error_message} onPress={showErrorDetails} /> : null}
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>;
|
</TouchableOpacity>;
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +127,18 @@ function DiscordPresence(props: {
|
||||||
</View>;
|
</View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DiscordPresenceError(props: {
|
||||||
|
message: string;
|
||||||
|
onPress?: () => void;
|
||||||
|
}) {
|
||||||
|
return <TouchableOpacity onPress={props.onPress} style={styles.errorTouchable}>
|
||||||
|
<View style={styles.error}>
|
||||||
|
<Text style={styles.icon}><Warning /></Text>
|
||||||
|
<Text style={styles.errorText} numberOfLines={1} ellipsizeMode="tail">{props.message}</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableOpacity>;
|
||||||
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
discord: {
|
discord: {
|
||||||
backgroundColor: DISCORD_COLOUR,
|
backgroundColor: DISCORD_COLOUR,
|
||||||
|
|
@ -168,4 +196,23 @@ const styles = StyleSheet.create({
|
||||||
discordUserDiscriminator: {
|
discordUserDiscriminator: {
|
||||||
opacity: 0.7,
|
opacity: 0.7,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
errorTouchable: {
|
||||||
|
marginVertical: -16,
|
||||||
|
marginHorizontal: -20,
|
||||||
|
marginTop: 6,
|
||||||
|
paddingVertical: 16,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingTop: 10,
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
marginRight: 10,
|
||||||
|
color: TEXT_COLOUR_DARK,
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
color: TEXT_COLOUR_DARK,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,10 @@ export interface DiscordPresenceExternalMonitorsConfiguration {
|
||||||
enable_splatnet3_monitoring?: boolean;
|
enable_splatnet3_monitoring?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DiscordStatus {
|
||||||
|
error_message: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LoginItem {
|
export interface LoginItem {
|
||||||
supported: boolean;
|
supported: boolean;
|
||||||
startup_enabled: boolean;
|
startup_enabled: boolean;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { createModalWindow, getWindowConfiguration, setWindowHeight } from './wi
|
||||||
import { askAddNsoAccount, askAddPctlAccount } from './na-auth.js';
|
import { askAddNsoAccount, askAddPctlAccount } from './na-auth.js';
|
||||||
import { App } from './index.js';
|
import { App } from './index.js';
|
||||||
import { EmbeddedPresenceMonitor } from './monitor.js';
|
import { EmbeddedPresenceMonitor } from './monitor.js';
|
||||||
import { DiscordPresenceConfiguration, DiscordPresenceSource, LoginItemOptions, WindowType } from '../common/types.js';
|
import { DiscordPresenceConfiguration, DiscordPresenceSource, DiscordStatus, LoginItemOptions, WindowType } from '../common/types.js';
|
||||||
import { CurrentUser, Friend, Game, PresenceState, WebService } from '../../api/coral-types.js';
|
import { CurrentUser, Friend, Game, PresenceState, WebService } from '../../api/coral-types.js';
|
||||||
import { NintendoAccountUser } from '../../api/na.js';
|
import { NintendoAccountUser } from '../../api/na.js';
|
||||||
import createDebug from '../../util/debug.js';
|
import createDebug from '../../util/debug.js';
|
||||||
|
|
@ -109,6 +109,8 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) {
|
||||||
handle('discord:source', () => appinstance.monitors.getDiscordPresenceSource());
|
handle('discord:source', () => appinstance.monitors.getDiscordPresenceSource());
|
||||||
handle('discord:setsource', (e, source: DiscordPresenceSource | null) => appinstance.monitors.setDiscordPresenceSource(source));
|
handle('discord:setsource', (e, source: DiscordPresenceSource | null) => appinstance.monitors.setDiscordPresenceSource(source));
|
||||||
handle('discord:presence', () => appinstance.monitors.getDiscordPresence());
|
handle('discord:presence', () => appinstance.monitors.getDiscordPresence());
|
||||||
|
handle('discord:status', () => appinstance.monitors.getDiscordStatus());
|
||||||
|
handle('discord:showerror', () => appinstance.monitors.showDiscordPresenceLastUpdateError());
|
||||||
handle('discord:user', () => appinstance.monitors.getActiveDiscordPresenceMonitor()?.discord.rpc?.client.user ?? null);
|
handle('discord:user', () => appinstance.monitors.getActiveDiscordPresenceMonitor()?.discord.rpc?.client.user ?? null);
|
||||||
handle('discord:users', async () => {
|
handle('discord:users', async () => {
|
||||||
const users: User[] = [];
|
const users: User[] = [];
|
||||||
|
|
@ -173,6 +175,7 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) {
|
||||||
store.on('update-discord-presence-source', () => sendToAllWindows('nxapi:discord:shouldrefresh'));
|
store.on('update-discord-presence-source', () => sendToAllWindows('nxapi:discord:shouldrefresh'));
|
||||||
store.on('update-discord-presence', (p: DiscordPresence) => sendToAllWindows('nxapi:discord:presence', p));
|
store.on('update-discord-presence', (p: DiscordPresence) => sendToAllWindows('nxapi:discord:presence', p));
|
||||||
store.on('update-discord-user', (u: User) => sendToAllWindows('nxapi:discord:user', u));
|
store.on('update-discord-user', (u: User) => sendToAllWindows('nxapi:discord:user', u));
|
||||||
|
store.on('update-discord-status', (s: DiscordStatus | null) => sendToAllWindows('nxapi:discord:status', s));
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendToAllWindows(channel: string, ...args: any[]) {
|
function sendToAllWindows(channel: string, ...args: any[]) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { Notification } from './electron.js';
|
import { Notification } from './electron.js';
|
||||||
import { App } from './index.js';
|
import { App } from './index.js';
|
||||||
import { showErrorDialog, tryGetNativeImageFromUrl } from './util.js';
|
import { showErrorDialog, tryGetNativeImageFromUrl } from './util.js';
|
||||||
import { DiscordPresenceConfiguration, DiscordPresenceExternalMonitorsConfiguration, DiscordPresenceSource } from '../common/types.js';
|
import { DiscordPresenceConfiguration, DiscordPresenceExternalMonitorsConfiguration, DiscordPresenceSource, DiscordStatus } from '../common/types.js';
|
||||||
import { CurrentUser, Friend, Game, CoralError } from '../../api/coral-types.js';
|
import { CurrentUser, Friend, Game, CoralError } from '../../api/coral-types.js';
|
||||||
import { ErrorResponse } from '../../api/util.js';
|
import { ErrorResponse } from '../../api/util.js';
|
||||||
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
|
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
|
||||||
|
|
@ -11,6 +11,7 @@ import { LoopResult } from '../../util/loop.js';
|
||||||
import { DiscordPresence, DiscordPresencePlayTime, ErrorResult } from '../../discord/types.js';
|
import { DiscordPresence, DiscordPresencePlayTime, ErrorResult } from '../../discord/types.js';
|
||||||
import { DiscordRpcClient } from '../../discord/rpc.js';
|
import { DiscordRpcClient } from '../../discord/rpc.js';
|
||||||
import SplatNet3Monitor, { getConfigFromAppConfig as getSplatNet3MonitorConfigFromAppConfig } from '../../discord/monitor/splatoon3.js';
|
import SplatNet3Monitor, { getConfigFromAppConfig as getSplatNet3MonitorConfigFromAppConfig } from '../../discord/monitor/splatoon3.js';
|
||||||
|
import { ErrorDescription } from '../../util/errors.js';
|
||||||
|
|
||||||
const debug = createDebug('app:main:monitor');
|
const debug = createDebug('app:main:monitor');
|
||||||
|
|
||||||
|
|
@ -68,6 +69,19 @@ export class PresenceMonitorManager {
|
||||||
return ErrorResult.IGNORE;
|
return ErrorResult.IGNORE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
i.discord.onUpdateError = err => {
|
||||||
|
const status: DiscordStatus = {
|
||||||
|
error_message: err instanceof Error ?
|
||||||
|
err.name + ': ' + err.message :
|
||||||
|
ErrorDescription.getErrorDescription(err),
|
||||||
|
};
|
||||||
|
this.app.store.emit('update-discord-status', status);
|
||||||
|
};
|
||||||
|
i.discord.onUpdateSuccess = () => {
|
||||||
|
const status: DiscordStatus = {error_message: null};
|
||||||
|
this.app.store.emit('update-discord-status', status);
|
||||||
|
};
|
||||||
|
|
||||||
i.onError = err => this.handleError(i, err);
|
i.onError = err => this.handleError(i, err);
|
||||||
|
|
||||||
this.monitors.push(i);
|
this.monitors.push(i);
|
||||||
|
|
@ -97,6 +111,19 @@ export class PresenceMonitorManager {
|
||||||
this.app.store.emit('update-discord-user', client?.user ?? null);
|
this.app.store.emit('update-discord-user', client?.user ?? null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
i.discord.onUpdateError = err => {
|
||||||
|
const status: DiscordStatus = {
|
||||||
|
error_message: err instanceof Error ?
|
||||||
|
err.name + ': ' + err.message :
|
||||||
|
ErrorDescription.getErrorDescription(err),
|
||||||
|
};
|
||||||
|
this.app.store.emit('update-discord-status', status);
|
||||||
|
};
|
||||||
|
i.discord.onUpdateSuccess = () => {
|
||||||
|
const status: DiscordStatus = {error_message: null};
|
||||||
|
this.app.store.emit('update-discord-status', status);
|
||||||
|
};
|
||||||
|
|
||||||
i.onError = err => this.handleError(i, err);
|
i.onError = err => this.handleError(i, err);
|
||||||
|
|
||||||
this.monitors.push(i);
|
this.monitors.push(i);
|
||||||
|
|
@ -317,6 +344,8 @@ export class PresenceMonitorManager {
|
||||||
this.app.store.saveMonitorState(this);
|
this.app.store.saveMonitorState(this);
|
||||||
this.app.menu?.updateMenu();
|
this.app.menu?.updateMenu();
|
||||||
this.app.store.emit('update-discord-presence-source', source);
|
this.app.store.emit('update-discord-presence-source', source);
|
||||||
|
} else {
|
||||||
|
this.app.store.emit('update-discord-status', null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +389,29 @@ export class PresenceMonitorManager {
|
||||||
|
|
||||||
return LoopResult.OK;
|
return LoopResult.OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getDiscordStatus(): Promise<DiscordStatus | null> {
|
||||||
|
const monitor = this.getActiveDiscordPresenceMonitor();
|
||||||
|
if (!monitor) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
error_message: monitor.discord.last_update_error ?
|
||||||
|
monitor.discord.last_update_error instanceof Error ?
|
||||||
|
monitor.discord.last_update_error.name + ': ' + monitor.discord.last_update_error.message :
|
||||||
|
ErrorDescription.getErrorDescription(monitor.discord.last_update_error) : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async showDiscordPresenceLastUpdateError() {
|
||||||
|
const monitor = this.getActiveDiscordPresenceMonitor();
|
||||||
|
const error = monitor?.discord.last_update_error;
|
||||||
|
if (!error) return;
|
||||||
|
|
||||||
|
await showErrorDialog({
|
||||||
|
message: error.name + ' updating presence monitor',
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
|
||||||
import createDebug from 'debug';
|
import createDebug from 'debug';
|
||||||
import type { User } from 'discord-rpc';
|
import type { User } from 'discord-rpc';
|
||||||
import type { SharingItem } from '../main/electron.js';
|
import type { SharingItem } from '../main/electron.js';
|
||||||
import type { DiscordPresenceConfiguration, DiscordPresenceSource, LoginItem, LoginItemOptions, WindowConfiguration } from '../common/types.js';
|
import type { DiscordPresenceConfiguration, DiscordPresenceSource, DiscordStatus, LoginItem, LoginItemOptions, WindowConfiguration } from '../common/types.js';
|
||||||
import type { SavedToken } from '../../common/auth/coral.js';
|
import type { SavedToken } from '../../common/auth/coral.js';
|
||||||
import type { SavedMoonToken } from '../../common/auth/moon.js';
|
import type { SavedMoonToken } from '../../common/auth/moon.js';
|
||||||
import type { UpdateCacheData } from '../../common/update.js';
|
import type { UpdateCacheData } from '../../common/update.js';
|
||||||
|
|
@ -74,6 +74,8 @@ const ipc = {
|
||||||
getDiscordPresenceSource: () => inv<DiscordPresenceSource | null>('discord:source'),
|
getDiscordPresenceSource: () => inv<DiscordPresenceSource | null>('discord:source'),
|
||||||
setDiscordPresenceSource: (source: DiscordPresenceSource | null) => inv<void>('discord:setsource', source),
|
setDiscordPresenceSource: (source: DiscordPresenceSource | null) => inv<void>('discord:setsource', source),
|
||||||
getDiscordPresence: () => inv<DiscordPresence | null>('discord:presence'),
|
getDiscordPresence: () => inv<DiscordPresence | null>('discord:presence'),
|
||||||
|
getDiscordStatus: () => inv<DiscordStatus | null>('discord:status'),
|
||||||
|
showDiscordLastUpdateError: () => inv('discord:showerror'),
|
||||||
getDiscordUser: () => inv<User | null>('discord:user'),
|
getDiscordUser: () => inv<User | null>('discord:user'),
|
||||||
getDiscordUsers: () => inv<User[]>('discord:users'),
|
getDiscordUsers: () => inv<User[]>('discord:users'),
|
||||||
|
|
||||||
|
|
@ -109,6 +111,7 @@ ipcRenderer.on('nxapi:accounts:shouldrefresh', () => events.emit('update-nintend
|
||||||
ipcRenderer.on('nxapi:discord:shouldrefresh', () => events.emit('update-discord-presence-source'));
|
ipcRenderer.on('nxapi:discord:shouldrefresh', () => events.emit('update-discord-presence-source'));
|
||||||
ipcRenderer.on('nxapi:discord:presence', (e, p: DiscordPresence) => events.emit('update-discord-presence', p));
|
ipcRenderer.on('nxapi:discord:presence', (e, p: DiscordPresence) => events.emit('update-discord-presence', p));
|
||||||
ipcRenderer.on('nxapi:discord:user', (e, u: User) => events.emit('update-discord-user', u));
|
ipcRenderer.on('nxapi:discord:user', (e, u: User) => events.emit('update-discord-user', u));
|
||||||
|
ipcRenderer.on('nxapi:discord:status', (e, s: DiscordStatus | null) => events.emit('update-discord-status', s));
|
||||||
|
|
||||||
let accent_colour: string | undefined = invSync('systemPreferences:accent-colour');
|
let accent_colour: string | undefined = invSync('systemPreferences:accent-colour');
|
||||||
ipcRenderer.on('nxapi:systemPreferences:accent-colour', (event, c) => {
|
ipcRenderer.on('nxapi:systemPreferences:accent-colour', (event, c) => {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ class ZncDiscordPresenceClient {
|
||||||
ErrorResult | Promise<ErrorResult>) | null = null;
|
ErrorResult | Promise<ErrorResult>) | null = null;
|
||||||
|
|
||||||
update_presence_errors = 0;
|
update_presence_errors = 0;
|
||||||
|
last_update_error: Error | null = null;
|
||||||
|
last_update_error_at: Date | null = null;
|
||||||
|
onUpdateError: ((error: Error | null) => void) | null = null;
|
||||||
|
onUpdateSuccess: (() => void) | null = null;
|
||||||
|
onUpdate: (() => void) | null = null;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
readonly m: ZncDiscordPresence | ZncProxyDiscordPresence,
|
readonly m: ZncDiscordPresence | ZncProxyDiscordPresence,
|
||||||
|
|
@ -63,6 +68,13 @@ class ZncDiscordPresenceClient {
|
||||||
this.last_friendcode = friendcode;
|
this.last_friendcode = friendcode;
|
||||||
this.last_event = activeevent;
|
this.last_event = activeevent;
|
||||||
|
|
||||||
|
this.onUpdate?.call(null);
|
||||||
|
|
||||||
|
if (this.update_presence_errors) {
|
||||||
|
this.update_presence_errors = 0;
|
||||||
|
this.onUpdateSuccess?.call(null);
|
||||||
|
}
|
||||||
|
|
||||||
const online = presence?.state === PresenceState.ONLINE || presence?.state === PresenceState.PLAYING;
|
const online = presence?.state === PresenceState.ONLINE || presence?.state === PresenceState.PLAYING;
|
||||||
|
|
||||||
const show_presence =
|
const show_presence =
|
||||||
|
|
@ -328,15 +340,15 @@ class ZncDiscordPresenceClient {
|
||||||
|
|
||||||
async onError(err: Error) {
|
async onError(err: Error) {
|
||||||
this.update_presence_errors++;
|
this.update_presence_errors++;
|
||||||
|
this.last_update_error = err;
|
||||||
|
this.last_update_error_at = new Date();
|
||||||
|
|
||||||
if (this.update_presence_errors > 2) {
|
this.onUpdateError?.call(null, err);
|
||||||
|
|
||||||
|
if (this.update_presence_errors > 2 && this.rpc) {
|
||||||
// Disconnect from Discord if the last two attempts to update presence failed
|
// Disconnect from Discord if the last two attempts to update presence failed
|
||||||
// This prevents the user's activity on Discord being stuck
|
// This prevents the user's activity on Discord being stuck
|
||||||
if (this.rpc) {
|
this.setActivity(this.m.discord_preconnect ? this.rpc.id : null);
|
||||||
const client = this.rpc.client;
|
|
||||||
this.rpc = null;
|
|
||||||
await client.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.update_presence_errors > 10) {
|
if (this.update_presence_errors > 10) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user