mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-08-28 13:50:51 -05:00
Improve showing errors from main window
This commit is contained in:
@@ -9,7 +9,7 @@ import Section from './section.js';
|
||||
import { Button } from '../components/index.js';
|
||||
|
||||
export default function Event(props: {
|
||||
user: User;
|
||||
user: User<true>;
|
||||
event: ActiveEvent;
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
@@ -21,7 +21,9 @@ export default function Event(props: {
|
||||
const event_members = props.event.members.filter(m => m.isPlaying).length;
|
||||
const voip_members = props.event.members.filter(m => m.isJoinedVoip).length;
|
||||
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error}>
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error}
|
||||
errorKey={[props.user.nsotoken, 'activeevent']}
|
||||
>
|
||||
<View style={styles.content}>
|
||||
<Image source={{uri: props.event.imageUri, width: 100, height: 100}} style={styles.image} />
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import AddOutline from '../components/icons/add-outline.js';
|
||||
import { FriendCode } from '../components/index.js';
|
||||
|
||||
export default function Friends(props: {
|
||||
user: User;
|
||||
user: User<true>;
|
||||
friends: Friend_4[];
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
@@ -27,7 +27,10 @@ export default function Friends(props: {
|
||||
<Text style={[styles.icon, {color: '#' + accent_colour}]}><AddOutline title={t('add')!} /></Text>
|
||||
</TouchableOpacity>;
|
||||
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error} headerButtons={header_buttons}>
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error}
|
||||
errorKey={[props.user.nsotoken, 'friends']}
|
||||
headerButtons={header_buttons}
|
||||
>
|
||||
{props.friends.length ? <ScrollView horizontal>
|
||||
<View style={styles.content}>
|
||||
{props.friends.map(f => <Friend key={f.nsaId} friend={f} user={props.user} />)}
|
||||
|
||||
@@ -56,9 +56,7 @@ export default function Main(props: {
|
||||
useEventListener(events, 'window:refresh', refresh, []);
|
||||
|
||||
const showErrorDetails = useCallback(() => {
|
||||
if (friends_error) alert(friends_error.stack ?? friends_error.message);
|
||||
if (webservices_error) alert(webservices_error.stack ?? webservices_error.message);
|
||||
if (active_event_error) alert(active_event_error.stack ?? active_event_error.message);
|
||||
ipc.showCoralErrors(props.user.nsotoken!, ['friends', 'webservices', 'activeevent']);
|
||||
}, [friends_error, webservices_error, active_event_error]);
|
||||
|
||||
if (!friends || !webservices || !active_event) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ActivityIndicator, StyleSheet, Text, TouchableOpacity, View } from 'rea
|
||||
import { useAccentColour, useColourScheme } from '../util.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BORDER_COLOUR_LIGHT, BORDER_COLOUR_SECONDARY_DARK, TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
|
||||
import type { CachedErrorKey } from '../../main/ipc.js';
|
||||
import ipc from '../ipc.js';
|
||||
import Warning from '../components/icons/warning.js';
|
||||
|
||||
@@ -10,6 +11,7 @@ export default function Section(props: React.PropsWithChildren<{
|
||||
title: string;
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
errorKey?: [string, CachedErrorKey];
|
||||
headerButtons?: React.ReactNode;
|
||||
}>) {
|
||||
const theme = useColourScheme() === 'light' ? light : dark;
|
||||
@@ -17,7 +19,7 @@ export default function Section(props: React.PropsWithChildren<{
|
||||
const { t, i18n } = useTranslation('main_window', { keyPrefix: 'main_section' });
|
||||
|
||||
const showErrorDetails = useCallback(() => {
|
||||
alert(props.error);
|
||||
props.errorKey ? ipc.showCoralErrors(...props.errorKey) : alert(props.error);
|
||||
}, [props.error]);
|
||||
|
||||
return <View style={[styles.container, theme.container]}>
|
||||
|
||||
@@ -8,7 +8,7 @@ import { TEXT_COLOUR_DARK, TEXT_COLOUR_LIGHT } from '../constants.js';
|
||||
import Section from './section.js';
|
||||
|
||||
export default function WebServices(props: {
|
||||
user: User;
|
||||
user: User<true>;
|
||||
webservices: WebService[];
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
@@ -17,7 +17,9 @@ export default function WebServices(props: {
|
||||
|
||||
if (!props.webservices.length) return null;
|
||||
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error}>
|
||||
return <Section title={t('title')} loading={props.loading} error={props.error}
|
||||
errorKey={[props.user.nsotoken, 'webservices']}
|
||||
>
|
||||
<ScrollView horizontal>
|
||||
<View style={styles.content}>
|
||||
{props.webservices.map(g => <WebService key={g.id} webservice={g} token={props.user.nsotoken} />)}
|
||||
|
||||
@@ -250,9 +250,12 @@ export interface User<IsCoral extends boolean = boolean> {
|
||||
NintendoAccountUserCoral | NintendoAccountUserMoon;
|
||||
nso:
|
||||
IsCoral extends true ? SavedToken :
|
||||
IsCoral extends true ? null :
|
||||
IsCoral extends false ? null :
|
||||
SavedToken | null;
|
||||
nsotoken: string | undefined;
|
||||
nsotoken:
|
||||
IsCoral extends true ? string :
|
||||
IsCoral extends false ? null :
|
||||
string | null;
|
||||
moon: SavedMoonToken | null;
|
||||
moontoken: string | undefined;
|
||||
}
|
||||
@@ -263,7 +266,7 @@ export async function getAccounts() {
|
||||
const accounts: User[] = [];
|
||||
|
||||
for (const id of ids ?? []) {
|
||||
const nsotoken = await ipc.getNintendoAccountCoralToken(id);
|
||||
const nsotoken = await ipc.getNintendoAccountCoralToken(id) ?? null;
|
||||
const moontoken = await ipc.getNintendoAccountMoonToken(id);
|
||||
|
||||
const nso = nsotoken ? await ipc.getSavedCoralToken(nsotoken) ?? null : null;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { BrowserWindow, clipboard, IpcMain, IpcMainInvokeEvent, KeyboardEvent, Menu, MenuItem, ShareMenu, SharingItem, shell, systemPreferences } from 'electron';
|
||||
import { User } from 'discord-rpc';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import { ErrorDescription, ErrorDescriptionSymbol, HasErrorDescription } from '../../util/errors.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import openWebService, { handleOpenWebServiceError, QrCodeReaderOptions, WebServiceIpc, WebServiceValidationError } from './webservices.js';
|
||||
import { createModalWindow, getWindowConfiguration, setWindowHeight } from './windows.js';
|
||||
import { askAddNsoAccount, askAddPctlAccount } from './na-auth.js';
|
||||
@@ -7,19 +10,42 @@ import { App } from './index.js';
|
||||
import { EmbeddedPresenceMonitor } from './monitor.js';
|
||||
import { DiscordPresenceConfiguration, DiscordPresenceSource, DiscordStatus, LoginItemOptions, WindowType } from '../common/types.js';
|
||||
import { CurrentUser, Friend, Game, PresenceState, WebService } from '../../api/coral-types.js';
|
||||
import { NintendoAccountUser } from '../../api/na.js';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload, NintendoAccountUser } from '../../api/na.js';
|
||||
import { DiscordPresence } from '../../discord/types.js';
|
||||
import { getDiscordRpcClients } from '../../discord/rpc.js';
|
||||
import { defaultTitle } from '../../discord/titles.js';
|
||||
import type { FriendProps } from '../browser/friend/index.js';
|
||||
import type { DiscordSetupProps } from '../browser/discord/index.js';
|
||||
import type { AddFriendProps } from '../browser/add-friend/index.js';
|
||||
import { CoralUser } from '../../common/users.js';
|
||||
import { MembershipRequiredError } from '../../common/auth/util.js';
|
||||
import { ErrorDescription, ErrorDescriptionSymbol, HasErrorDescription } from '../../util/errors.js';
|
||||
import { showErrorDialog } from './util.js';
|
||||
|
||||
const debug = createDebug('app:main:ipc');
|
||||
|
||||
export type CachedErrorKey = 'user' | 'announcements' | 'friends' | 'webservices' | 'activeevent' | 'friendcodeurl' | 'friendrequests-received' | 'friendrequests-sent';
|
||||
const cached_errors = new Map<string, Map<CachedErrorKey, Error>>();
|
||||
|
||||
function createErrorHandler(user: CoralUser<any>, key: CachedErrorKey): (err: Error) => void
|
||||
function createErrorHandler(na_session_token: string, key: CachedErrorKey): (err: Error) => void
|
||||
function createErrorHandler(user: CoralUser<any> | string, key: CachedErrorKey) {
|
||||
if (typeof user === 'string') {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(user);
|
||||
user = jwt.payload.sub;
|
||||
} else {
|
||||
user = user.data.user.id;
|
||||
}
|
||||
|
||||
return (err: Error) => {
|
||||
let errors = cached_errors.get(user);
|
||||
if (!errors) cached_errors.set(user, errors = new Map());
|
||||
|
||||
errors.set(key, err);
|
||||
|
||||
throw err;
|
||||
};
|
||||
}
|
||||
|
||||
export function setupIpc(appinstance: App, ipcMain: IpcMain) {
|
||||
const store = appinstance.store;
|
||||
const storage = appinstance.store.storage;
|
||||
@@ -81,20 +107,52 @@ export function setupIpc(appinstance: App, ipcMain: IpcMain) {
|
||||
handle('coral:gettoken', (e, id: string) => storage.getItem('NintendoAccountToken.' + id));
|
||||
handle('coral:getcachedtoken', (e, token: string) => storage.getItem('NsoToken.' + token));
|
||||
handle('coral:announcements', (e, token: string) => store.users.get(token).then(u => u.announcements.result));
|
||||
handle('coral:friends', (e, token: string) => store.users.get(token).then(u => u.getFriends()));
|
||||
handle('coral:webservices', (e, token: string) => store.users.get(token).then(u => u.getWebServices()));
|
||||
handle('coral:friends', (e, token: string) => store.users.get(token).then(u => u.getFriends())
|
||||
.catch(createErrorHandler(token, 'friends')));
|
||||
handle('coral:webservices', (e, token: string) => store.users.get(token).then(u => u.getWebServices())
|
||||
.catch(createErrorHandler(token, 'webservices')));
|
||||
handle('coral:openwebservice', (e, webservice: WebService, token: string, qs?: string) =>
|
||||
store.users.get(token).then(u => openWebService(store, token, u.nso, u.data, webservice, qs)
|
||||
.catch(err => err instanceof WebServiceValidationError || err instanceof MembershipRequiredError ?
|
||||
handleOpenWebServiceError(err, webservice, qs, u.data, BrowserWindow.fromWebContents(e.sender)!) :
|
||||
null)));
|
||||
handle('coral:activeevent', (e, token: string) => store.users.get(token).then(u => u.getActiveEvent()));
|
||||
handle('coral:friendcodeurl', (e, token: string) => store.users.get(token).then(u => u.nso.getFriendCodeUrl()));
|
||||
handle('coral:friendrequests:received', (e, token: string) => store.users.get(token).then(u => u.nso.getReceivedFriendRequests()));
|
||||
handle('coral:friendrequests:sent', (e, token: string) => store.users.get(token).then(u => u.nso.getSentFriendRequests()));
|
||||
handle('coral:activeevent', (e, token: string) => store.users.get(token).then(u => u.getActiveEvent()
|
||||
.then(e => e ?? {}))
|
||||
.catch(createErrorHandler(token, 'activeevent')));
|
||||
handle('coral:friendcodeurl', (e, token: string) => store.users.get(token).then(u => u.nso.getFriendCodeUrl())
|
||||
.catch(createErrorHandler(token, 'friendcodeurl')));
|
||||
handle('coral:friendrequests:received', (e, token: string) => store.users.get(token).then(u => u.getReceivedFriendRequests())
|
||||
.catch(createErrorHandler(token, 'friendrequests-received')));
|
||||
handle('coral:friendrequests:sent', (e, token: string) => store.users.get(token).then(u => u.getSentFriendRequests())
|
||||
.catch(createErrorHandler(token, 'friendrequests-sent')));
|
||||
handle('coral:friendcode', (e, token: string, friendcode: string, hash?: string) => store.users.get(token).then(u => u.nso.getUserByFriendCode(friendcode, hash)));
|
||||
handle('coral:addfriend', (e, token: string, nsaid: string) => store.users.get(token).then(u => u.addFriend(nsaid)));
|
||||
|
||||
handle('coral:showlasterrors', async (e, token: string, keys: CachedErrorKey | CachedErrorKey[]) => {
|
||||
const [jwt, sig] = Jwt.decode<NintendoAccountSessionTokenJwtPayload>(token);
|
||||
const user = jwt.payload.sub;
|
||||
const errors = cached_errors.get(user);
|
||||
if (!errors?.size) return;
|
||||
|
||||
if (typeof keys === 'string') keys = [keys];
|
||||
|
||||
const show = new Set<Error>();
|
||||
|
||||
for (const key of keys) {
|
||||
if (!errors.has(key)) continue;
|
||||
show.add(errors.get(key)!);
|
||||
}
|
||||
|
||||
for (const error of show) {
|
||||
showErrorDialog({
|
||||
message: 'Error loading data',
|
||||
error,
|
||||
app: appinstance,
|
||||
window: BrowserWindow.fromWebContents(e.sender) ?? undefined,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
handle('window:showpreferences', () => appinstance.showPreferencesWindow().id);
|
||||
handle('window:showfriend', (e, props: FriendProps) =>
|
||||
createModalWindow(WindowType.FRIEND, props, e.sender).id);
|
||||
|
||||
@@ -7,9 +7,9 @@ import type { SavedToken } from '../../common/auth/coral.js';
|
||||
import type { SavedMoonToken } from '../../common/auth/moon.js';
|
||||
import type { UpdateCacheData } from '../../common/update.js';
|
||||
import type { StatusUpdate } from '../../common/status.js';
|
||||
import type { Announcements, Announcements_4, CoralSuccessResponse, CurrentUser, Friend, Friend_4, FriendCodeUrl, FriendCodeUser, GetActiveEventResult, ReceivedFriendRequests, SentFriendRequests, WebService, WebServices, WebServices_4 } from '../../api/coral-types.js';
|
||||
import type { Announcements_4, CoralSuccessResponse, CurrentUser, Friend, Friend_4, FriendCodeUrl, FriendCodeUser, GetActiveEventResult, ReceivedFriendRequests, SentFriendRequests, WebService, WebServices_4 } from '../../api/coral-types.js';
|
||||
import type { DiscordPresence } from '../../discord/types.js';
|
||||
import type { NintendoAccountUser } from '../../api/na.js';
|
||||
import type { CachedErrorKey } from '../main/ipc.js';
|
||||
import type { DiscordSetupProps } from '../browser/discord/index.js';
|
||||
import type { FriendProps } from '../browser/friend/index.js';
|
||||
import type { AddFriendProps } from '../browser/add-friend/index.js';
|
||||
@@ -72,6 +72,8 @@ const ipc = {
|
||||
getNsoUserByFriendCode: (token: string, friendcode: string, hash?: string) => inv<FriendCodeUser>('coral:friendcode', token, friendcode, hash),
|
||||
addNsoFriend: (token: string, nsa_id: string) => inv<{result: CoralSuccessResponse<{}>; friend: Friend | null}>('coral:addfriend', token, nsa_id),
|
||||
|
||||
showCoralErrors: (token: string, keys: CachedErrorKey | CachedErrorKey[]) => inv('coral:showlasterrors', token, keys),
|
||||
|
||||
getDiscordPresenceConfig: () => inv<DiscordPresenceConfiguration | null>('discord:config'),
|
||||
setDiscordPresenceConfig: (config: DiscordPresenceConfiguration | null) => inv<void>('discord:setconfig', config),
|
||||
getDiscordPresenceOptions: () => inv<Omit<DiscordPresenceConfiguration, 'source'> | null>('discord:options'),
|
||||
@@ -100,7 +102,7 @@ const ipc = {
|
||||
showUserMenu: (user: NintendoAccountUserCoral | NintendoAccountUserMoon, nso?: CurrentUser, moon?: boolean) => inv('menu:user', user, nso, moon),
|
||||
showAddUserMenu: () => inv('menu:add-user'),
|
||||
showFriendCodeMenu: (fc: CurrentUser['links']['friendCode']) => inv('menu:friend-code', fc),
|
||||
showFriendMenu: (user: NintendoAccountUserCoral, nso: CurrentUser, friend: Friend) => inv('menu:friend', user, nso, friend),
|
||||
showFriendMenu: (user: NintendoAccountUserCoral, nso: CurrentUser, friend: Friend_4) => inv('menu:friend', user, nso, friend),
|
||||
|
||||
registerEventListener: (event: string, listener: (args: any[]) => void) => events.on(event, listener),
|
||||
removeEventListener: (event: string, listener: (args: any[]) => void) => events.removeListener(event, listener),
|
||||
|
||||
Reference in New Issue
Block a user