Skip to content

Commit 8788c8f

Browse files
authored
Error boundary messaging (#182)
* Update messaging on error boundary * Update messaging * Tweak function name * Add restart app button
1 parent 0a25236 commit 8788c8f

File tree

5 files changed

+177
-62
lines changed

5 files changed

+177
-62
lines changed

main/api/updates/index.ts

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import axios from "axios";
22
import { app } from "electron";
33
import log from "electron-log";
4+
import { clean, gt } from "semver";
45

56
import { PartialGithubRelease } from "../../../shared/types";
67
import { t } from "../trpc";
@@ -16,52 +17,67 @@ let cachedResult:
1617
| { ok: true; data: ReadonlyArray<PartialGithubRelease> }
1718
| { ok: false; data: string } = { ok: false, data: "" };
1819

20+
async function getLatestReleases() {
21+
const now = performance.now();
22+
23+
if (now - cacheTime > lastAttempt) {
24+
lastAttempt = now;
25+
log.log("Fetching releases from GitHub");
26+
await axios
27+
.get(githubReleaseUrl, {
28+
headers: {
29+
accept: "application/vnd.github+json",
30+
"X-GitHub-Api-Version": "2022-11-28",
31+
},
32+
})
33+
.then((response) => {
34+
cachedResult = { ok: true, data: response.data };
35+
})
36+
.catch((error) => {
37+
let errorData;
38+
if (error.response) {
39+
// The request was made and the server responded with a status code
40+
// that falls out of the range of 2xx
41+
errorData = JSON.stringify(error.response.data);
42+
} else if (error.request) {
43+
// The request was made but no response was received
44+
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
45+
// http.ClientRequest in node.js
46+
errorData = JSON.stringify(error.request);
47+
} else {
48+
errorData = JSON.stringify(error.message);
49+
}
50+
51+
log.error("An error occurred while fetching update notes:", errorData);
52+
cachedResult = { ok: false, data: errorData };
53+
});
54+
}
55+
56+
return cachedResult;
57+
}
58+
1959
export const updateRouter = t.router({
2060
getCurrentVersion: t.procedure.query(async () => {
2161
return app.getVersion();
2262
}),
23-
getUpdateNotes: t.procedure.query(async () => {
24-
const now = performance.now();
63+
isUpdateAvailable: t.procedure.query(async () => {
64+
const latestRelease = await getLatestReleases();
2565

26-
if (now - cacheTime > lastAttempt) {
27-
lastAttempt = now;
28-
log.log("Fetching releases from GitHub");
29-
await axios
30-
.get(githubReleaseUrl, {
31-
headers: {
32-
accept: "application/vnd.github+json",
33-
"X-GitHub-Api-Version": "2022-11-28",
34-
},
35-
})
36-
.then((response) => {
37-
cachedResult = { ok: true, data: response.data };
38-
})
39-
.catch((error) => {
40-
let errorData;
41-
if (error.response) {
42-
// The request was made and the server responded with a status code
43-
// that falls out of the range of 2xx
44-
errorData = JSON.stringify(error.response.data);
45-
} else if (error.request) {
46-
// The request was made but no response was received
47-
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
48-
// http.ClientRequest in node.js
49-
errorData = JSON.stringify(error.request);
50-
} else {
51-
errorData = JSON.stringify(error.message);
52-
}
66+
if (!latestRelease.ok) return false;
5367

54-
log.error(
55-
"An error occurred while fetching update notes:",
56-
errorData,
57-
);
58-
cachedResult = { ok: false, data: errorData };
59-
});
60-
}
68+
const latestVersion = clean(latestRelease.data[0].tag_name);
6169

62-
if (cachedResult.ok) {
63-
return cachedResult.data;
70+
if (!latestVersion) return false;
71+
72+
return gt(latestVersion, app.getVersion());
73+
}),
74+
getUpdateNotes: t.procedure.query(async () => {
75+
const latestRelease = await getLatestReleases();
76+
77+
if (latestRelease.ok) {
78+
return latestRelease.data;
6479
}
65-
throw new Error(cachedResult.data);
80+
81+
throw new Error(latestRelease.data);
6682
}),
6783
});

package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"react-intl": "^6.5.5",
8787
"react-markdown": "9.0.1",
8888
"react-virtuoso": "^4.6.2",
89+
"semver": "^7.6.0",
8990
"tar": "^6.2.0",
9091
"tsx": "^4.6.2",
9192
"type-fest": "^4.6.0",

renderer/components/ErrorBoundary/ErrorBoundary.tsx

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import { CopyIcon } from "@chakra-ui/icons";
2-
import { Box, Flex, Text, Heading, Code, HStack } from "@chakra-ui/react";
2+
import {
3+
Box,
4+
Flex,
5+
Text,
6+
Heading,
7+
Code,
8+
HStack,
9+
chakra,
10+
Grid,
11+
ListItem,
12+
OrderedList,
13+
} from "@chakra-ui/react";
314
import log from "electron-log";
415
import { Component, ErrorInfo, ReactNode } from "react";
516
import { defineMessages, useIntl } from "react-intl";
@@ -8,6 +19,7 @@ import { useCopyToClipboard } from "usehooks-ts";
819
import { WithDraggableArea } from "@/layouts/WithDraggableArea";
920
import { trpcReact } from "@/providers/TRPCProvider";
1021
import { COLORS } from "@/ui/colors";
22+
import { PillButton } from "@/ui/PillButton/PillButton";
1123
import { useIFToast } from "@/ui/Toast/Toast";
1224

1325
import { ResetNodeButton } from "../NodeSettings/NodeSettings";
@@ -16,16 +28,33 @@ const messages = defineMessages({
1628
errorStateHeading: {
1729
defaultMessage: "Something went wrong",
1830
},
19-
errorStateDescription: {
20-
defaultMessage:
21-
"Please restart the app and try again. If the issue persists, let us know on Discord so we can help troubleshoot.",
22-
},
2331
errorMessage: {
2432
defaultMessage: "Error Message",
2533
},
2634
errorCopied: {
2735
defaultMessage: "Error copied to clipboard",
2836
},
37+
troubleshootingSteps: {
38+
defaultMessage: "Troubleshooting Steps",
39+
},
40+
restartApp: {
41+
defaultMessage: "Try restarting the app",
42+
},
43+
updateAvailable: {
44+
defaultMessage:
45+
"An app update is available. Wait for it to finish downloading and restart the app, or download the latest version from the <link>Iron Fish website</link>.",
46+
},
47+
resetNode: {
48+
defaultMessage:
49+
"Reset the node. You'll have to re-scan the blockchain, but your accounts and funds will not be affected.",
50+
},
51+
reportIssue: {
52+
defaultMessage:
53+
"If none of these steps help, please report the issue on <link>Discord</link>.",
54+
},
55+
restartAppButton: {
56+
defaultMessage: "Restart App",
57+
},
2958
});
3059

3160
type Props = {
@@ -68,16 +97,16 @@ function DefaultFallback({ error }: { error: Error }) {
6897
const [_, copyToClipboard] = useCopyToClipboard();
6998
const toast = useIFToast();
7099
const { mutate: relaunchApp } = trpcReact.relaunchApp.useMutation();
100+
101+
const { data: isUpdateAvailable } = trpcReact.isUpdateAvailable.useQuery();
102+
71103
return (
72104
<WithDraggableArea>
73105
<Flex h="100%" alignItems="center" justifyContent="center" p={4}>
74106
<Box maxW="100%" w="800px">
75107
<Heading as="h1" textAlign="center" mb={4}>
76108
{formatMessage(messages.errorStateHeading)}
77109
</Heading>
78-
<Text textAlign="center" fontSize="md" mb={6}>
79-
{formatMessage(messages.errorStateDescription)}
80-
</Text>
81110

82111
<HStack>
83112
<Text
@@ -108,19 +137,72 @@ function DefaultFallback({ error }: { error: Error }) {
108137
maxW="100%"
109138
w="100%"
110139
overflow="auto"
111-
mb={8}
140+
mb={6}
112141
>
113142
<Text as="pre">{error.message}</Text>
114143
</Code>
115144

116-
<ResetNodeButton
117-
buttonProps={{
118-
variant: "primary",
119-
}}
120-
onSuccess={() => {
121-
relaunchApp();
122-
}}
123-
/>
145+
<Grid gap={4} mb={6}>
146+
<Text fontWeight="bold" fontSize="md">
147+
{formatMessage(messages.troubleshootingSteps)}
148+
</Text>
149+
<OrderedList>
150+
<ListItem>{formatMessage(messages.restartApp)}</ListItem>
151+
{isUpdateAvailable && (
152+
<ListItem>
153+
{formatMessage(messages.updateAvailable, {
154+
link: (content: unknown) => {
155+
return (
156+
<chakra.span
157+
as="a"
158+
color={COLORS.LINK}
159+
href="https://ironfish.network/use/node-app"
160+
target="_blank"
161+
rel="noreferrer"
162+
_hover={{ textDecoration: "underline" }}
163+
>
164+
{Array.isArray(content) && content.at(0)}
165+
</chakra.span>
166+
);
167+
},
168+
})}
169+
</ListItem>
170+
)}
171+
<ListItem>{formatMessage(messages.resetNode)}</ListItem>
172+
<ListItem>
173+
{formatMessage(messages.reportIssue, {
174+
link: (content: unknown) => {
175+
return (
176+
<chakra.span
177+
as="a"
178+
color={COLORS.LINK}
179+
href="https://discord.ironfish.network/"
180+
target="_blank"
181+
rel="noreferrer"
182+
_hover={{ textDecoration: "underline" }}
183+
>
184+
{Array.isArray(content) && content.at(0)}
185+
</chakra.span>
186+
);
187+
},
188+
})}
189+
</ListItem>
190+
</OrderedList>
191+
</Grid>
192+
193+
<HStack>
194+
<ResetNodeButton
195+
buttonProps={{
196+
variant: "inverted",
197+
}}
198+
onSuccess={() => {
199+
relaunchApp();
200+
}}
201+
/>
202+
<PillButton onClick={() => relaunchApp()}>
203+
{formatMessage(messages.restartAppButton)}
204+
</PillButton>
205+
</HStack>
124206
</Box>
125207
</Flex>
126208
</WithDraggableArea>

renderer/intl/locales/en-US.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@
108108
"6P/bTL": {
109109
"message": "Connection Type"
110110
},
111+
"6Zll8z": {
112+
"message": "Reset the node. You'll have to re-scan the blockchain, but your accounts and funds will not be affected."
113+
},
111114
"6q3tCn": {
112115
"message": "Connecting"
113116
},
@@ -228,6 +231,9 @@
228231
"HtgUU8": {
229232
"message": "Type ''{accountName}'' to remove"
230233
},
234+
"IwW0RC": {
235+
"message": "If none of these steps help, please report the issue on <link>Discord</link>."
236+
},
231237
"JJNc3c": {
232238
"message": "Previous"
233239
},
@@ -256,6 +262,9 @@
256262
"KN7zKn": {
257263
"message": "Error"
258264
},
265+
"KiaI4a": {
266+
"message": "Try restarting the app"
267+
},
259268
"Kkv03/": {
260269
"message": "Transaction Details"
261270
},
@@ -400,9 +409,6 @@
400409
"VzzYJk": {
401410
"message": "Create"
402411
},
403-
"WHjylD": {
404-
"message": "Please restart the app and try again. If the issue persists, let us know on Discord so we can help troubleshoot."
405-
},
406412
"WKCp0D": {
407413
"message": "Asset"
408414
},
@@ -466,6 +472,9 @@
466472
"bcOwoF": {
467473
"message": "Choose your language"
468474
},
475+
"bqjW+S": {
476+
"message": "Troubleshooting Steps"
477+
},
469478
"c/KktL": {
470479
"message": "Resources"
471480
},
@@ -553,6 +562,9 @@
553562
"kF9XUo": {
554563
"message": "Timestamp"
555564
},
565+
"kNJdlj": {
566+
"message": "Restart App"
567+
},
556568
"kTt/ND": {
557569
"message": "Russian"
558570
},
@@ -577,6 +589,9 @@
577589
"o0DpEu": {
578590
"message": "Unzipping..."
579591
},
592+
"oZO6n5": {
593+
"message": "An app update is available. Wait for it to finish downloading and restart the app, or download the latest version from the <link>Iron Fish website</link>."
594+
},
580595
"omnNfq": {
581596
"message": "About Fees"
582597
},

0 commit comments

Comments
 (0)