TrendTimelinePanel: improved error handling
This commit is contained in:
parent
971fb6a8e7
commit
f6c540a3ad
3 changed files with 69 additions and 53 deletions
|
@ -1,40 +1,59 @@
|
||||||
import { Button } from '@suid/material';
|
import { Button } from "@suid/material";
|
||||||
import {Component, createResource} from 'solid-js'
|
import { Component, createResource } from "solid-js";
|
||||||
import { css } from 'solid-styled';
|
import { css } from "solid-styled";
|
||||||
|
|
||||||
const UnexpectedError: Component<{ error?: any }> = (props) => {
|
const UnexpectedError: Component<{ error?: any }> = (props) => {
|
||||||
|
const [errorMsg] = createResource(
|
||||||
const [errorMsg] = createResource(() => props.error, async (err) => {
|
() => props.error,
|
||||||
|
async (err) => {
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
const mod = await import('stacktrace-js')
|
const mod = await import("stacktrace-js");
|
||||||
const stacktrace = await mod.fromError(err)
|
try {
|
||||||
const strackMsg = stacktrace.map(entry => `${entry.functionName ?? "<unknown>"}@${entry.fileName}:(${entry.lineNumber}:${entry.columnNumber})`).join('\n')
|
const stacktrace = await mod.fromError(err);
|
||||||
return `${err.name}: ${err.message}\n${strackMsg}`
|
const strackMsg = stacktrace
|
||||||
|
.map(
|
||||||
|
(entry) =>
|
||||||
|
`${entry.functionName ?? "<unknown>"}@${entry.fileName}:(${entry.lineNumber}:${entry.columnNumber})`,
|
||||||
|
)
|
||||||
|
.join("\n");
|
||||||
|
return `${err.name}: ${err.message}\n${strackMsg}`;
|
||||||
|
} catch (reason) {
|
||||||
|
return `<failed to build the stacktrace of "${err}"...>\n${reason}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err.toString()
|
return err.toString();
|
||||||
})
|
},
|
||||||
|
);
|
||||||
|
|
||||||
css`
|
css`
|
||||||
main {
|
main {
|
||||||
padding: calc(var(--safe-area-inset-top) + 20px) calc(var(--safe-area-inset-right) + 20px) calc(var(--safe-area-inset-bottom) + 20px) calc(var(--safe-area-inset-left) + 20px);
|
padding: calc(var(--safe-area-inset-top) + 20px)
|
||||||
|
calc(var(--safe-area-inset-right) + 20px)
|
||||||
|
calc(var(--safe-area-inset-bottom) + 20px)
|
||||||
|
calc(var(--safe-area-inset-left) + 20px);
|
||||||
}
|
}
|
||||||
`
|
`;
|
||||||
|
|
||||||
return <main>
|
return (
|
||||||
|
<main>
|
||||||
<h1>Oh, it is our fault.</h1>
|
<h1>Oh, it is our fault.</h1>
|
||||||
<p>There is an unexpected error in our app, and it's not your fault.</p>
|
<p>There is an unexpected error in our app, and it's not your fault.</p>
|
||||||
<p>You can reload to see if this guy is gone. If you meet this guy repeatly, please report to us.</p>
|
<p>
|
||||||
|
You can reload to see if this guy is gone. If you meet this guy
|
||||||
|
repeatly, please report to us.
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={() => window.location.reload()}>Reload</Button>
|
<Button onClick={() => window.location.reload()}>Reload</Button>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary>{errorMsg.loading ? 'Generating ' : " "}Technical Infomation (Bring to us if you report the problem)</summary>
|
<summary>
|
||||||
<pre>
|
{errorMsg.loading ? "Generating " : " "}Technical Infomation
|
||||||
{errorMsg()}
|
</summary>
|
||||||
</pre>
|
<pre>{errorMsg()}</pre>
|
||||||
</details>
|
</details>
|
||||||
</main>
|
</main>
|
||||||
}
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default UnexpectedError;
|
export default UnexpectedError;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { type mastodon } from "masto";
|
import { type mastodon } from "masto";
|
||||||
import { Accessor, createEffect, createResource } from "solid-js";
|
import { Accessor, catchError, createEffect, createResource } from "solid-js";
|
||||||
import { createStore } from "solid-js/store";
|
import { createStore } from "solid-js/store";
|
||||||
|
|
||||||
type TimelineFetchTips = {
|
type TimelineFetchTips = {
|
||||||
|
@ -125,7 +125,7 @@ export function createTimelineSnapshot(
|
||||||
const [snapshot, setSnapshot] = createStore([] as mastodon.v1.Status[][]);
|
const [snapshot, setSnapshot] = createStore([] as mastodon.v1.Status[][]);
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const nls = shot();
|
const nls = catchError(shot, (e) => console.error(e));
|
||||||
if (!nls) return;
|
if (!nls) return;
|
||||||
const ols = Array.from(snapshot);
|
const ols = Array.from(snapshot);
|
||||||
// The algorithm below assumes the snapshot is not changing
|
// The algorithm below assumes the snapshot is not changing
|
||||||
|
@ -154,10 +154,14 @@ export function createTimelineSnapshot(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return [snapshot, shot, {
|
return [
|
||||||
|
snapshot,
|
||||||
|
shot,
|
||||||
|
{
|
||||||
refetch,
|
refetch,
|
||||||
mutate: setSnapshot
|
mutate: setSnapshot,
|
||||||
}] as const;
|
},
|
||||||
|
] as const;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTimeline(timeline: Accessor<Timeline>) {
|
export function createTimeline(timeline: Accessor<Timeline>) {
|
||||||
|
|
|
@ -3,7 +3,6 @@ import {
|
||||||
For,
|
For,
|
||||||
onCleanup,
|
onCleanup,
|
||||||
createSignal,
|
createSignal,
|
||||||
Show,
|
|
||||||
untrack,
|
untrack,
|
||||||
Match,
|
Match,
|
||||||
Switch as JsSwitch,
|
Switch as JsSwitch,
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
createSelector,
|
createSelector,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { type mastodon } from "masto";
|
import { type mastodon } from "masto";
|
||||||
import { Button, LinearProgress } from "@suid/material";
|
import { Button } from "@suid/material";
|
||||||
import { createTimelineSnapshot } from "../masto/timelines.js";
|
import { createTimelineSnapshot } from "../masto/timelines.js";
|
||||||
import { vibrate } from "../platform/hardware.js";
|
import { vibrate } from "../platform/hardware.js";
|
||||||
import PullDownToRefresh from "./PullDownToRefresh.jsx";
|
import PullDownToRefresh from "./PullDownToRefresh.jsx";
|
||||||
|
@ -40,7 +39,7 @@ const TrendTimelinePanel: Component<{
|
||||||
|
|
||||||
const tlEndObserver = new IntersectionObserver(() => {
|
const tlEndObserver = new IntersectionObserver(() => {
|
||||||
if (untrack(() => props.prefetch) && !snapshot.loading)
|
if (untrack(() => props.prefetch) && !snapshot.loading)
|
||||||
refetchTimeline({ direction: "old" });
|
refetchTimeline();
|
||||||
});
|
});
|
||||||
|
|
||||||
onCleanup(() => tlEndObserver.disconnect());
|
onCleanup(() => tlEndObserver.disconnect());
|
||||||
|
@ -143,26 +142,19 @@ const TrendTimelinePanel: Component<{
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ref={(e) => tlEndObserver.observe(e)}></div>
|
<div ref={(e) => tlEndObserver.observe(e)}></div>
|
||||||
<Show when={snapshot.loading}>
|
|
||||||
<div
|
|
||||||
class="loading-line"
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<LinearProgress />
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
padding: "20px 0 calc(20px + var(--safe-area-inset-bottom, 0px))",
|
padding: "20px 0 calc(20px + var(--safe-area-inset-bottom, 0px))",
|
||||||
"align-items": "center",
|
"align-items": "center",
|
||||||
"justify-content": "center",
|
"justify-content": "center",
|
||||||
|
"flex-flow": "column",
|
||||||
|
gap: "20px"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<JsSwitch>
|
<JsSwitch>
|
||||||
<Match when={snapshot.error}>
|
<Match when={snapshot.error}>
|
||||||
|
<p>{`Oops: ${snapshot.error}`}</p>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={[refetchTimeline, undefined]}
|
onClick={[refetchTimeline, undefined]}
|
||||||
|
@ -170,6 +162,7 @@ const TrendTimelinePanel: Component<{
|
||||||
>
|
>
|
||||||
Retry
|
Retry
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<Button
|
<Button
|
||||||
|
|
Loading…
Reference in a new issue