init commit,
This commit is contained in:
165
99_references/voyager-main/src/core/AppCrash.tsx
Normal file
165
99_references/voyager-main/src/core/AppCrash.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import { styled } from "@linaria/react";
|
||||
import { FallbackProps } from "react-error-boundary";
|
||||
import { isInstalled, isNative } from "../helpers/device";
|
||||
import { IonButton, IonIcon, IonLabel } from "@ionic/react";
|
||||
import { logoGithub } from "ionicons/icons";
|
||||
import { unloadServiceWorkerAndRefresh } from "../helpers/serviceWorker";
|
||||
import { memoryHistory } from "../routes/common/Router";
|
||||
import store from "../store";
|
||||
import { loggedInSelector } from "../features/auth/authSelectors";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
padding-top: max(env(safe-area-inset-top), 8px);
|
||||
padding-right: max(env(safe-area-inset-right), 8px);
|
||||
padding-bottom: max(env(safe-area-inset-bottom), 8px);
|
||||
padding-left: max(env(safe-area-inset-left), 8px);
|
||||
|
||||
color: var(--ion-text-color);
|
||||
`;
|
||||
|
||||
const Title = styled.h2``;
|
||||
|
||||
const Description = styled.div``;
|
||||
|
||||
export default function AppCrash({ error }: FallbackProps) {
|
||||
// Don't use useLocation/useAppSelector, because they are not available
|
||||
// (`<AppCrash />` is at the root of the document tree)
|
||||
const location = memoryHistory ? memoryHistory.location : window.location;
|
||||
const loggedIn = loggedInSelector(store.getState());
|
||||
|
||||
let crashData = `
|
||||
### Crash description
|
||||
|
||||
<!-- Write any information here to help us debug your crash! -->
|
||||
<!-- What were you doing when the crash occurred? -->
|
||||
|
||||
### Device and app metadata
|
||||
|
||||
- window.location.href: \`${window.location.href}\`
|
||||
- react-router location.pathname: \`${location.pathname}\`
|
||||
- Logged in? \`${loggedIn}\`
|
||||
- Native app? \`${isNative()}\`
|
||||
- Installed to home screen? \`${isInstalled()}\`
|
||||
- Voyager version: \`${APP_VERSION}\`
|
||||
- BUILD_FOSS_ONLY: \`${BUILD_FOSS_ONLY}\`
|
||||
- User agent: \`${navigator.userAgent}\`
|
||||
|
||||
### Crash data
|
||||
|
||||
Error: \`\`${error}\`\`
|
||||
|
||||
#### Stack trace
|
||||
|
||||
\`\`\`
|
||||
`.trim();
|
||||
|
||||
crashData = `${crashData}\n${error instanceof Error ? error.stack : "Not available"}`;
|
||||
|
||||
async function clearData() {
|
||||
if (
|
||||
!confirm(
|
||||
"Are you sure? This will log you out of all accounts and delete all local app data including app configuration, hidden posts and favorites.",
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
|
||||
const dbs = await window.indexedDB.databases();
|
||||
for (const db of dbs) {
|
||||
if (db.name) window.indexedDB.deleteDatabase(db.name);
|
||||
}
|
||||
|
||||
alert("All data cleared.");
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Title>🫣 Gah! Voyager crashed!</Title>
|
||||
<Description>
|
||||
Voyager does not collect any data, so we would appreciate you
|
||||
voluntarily submitting this crash for us to investigate.
|
||||
</Description>
|
||||
<IonButton
|
||||
href={generateTruncatedCrashUrl(crashData)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="success"
|
||||
>
|
||||
<IonIcon icon={logoGithub} slot="start" />
|
||||
<IonLabel>Open Github issue with crash data</IonLabel>
|
||||
</IonButton>
|
||||
|
||||
<hr />
|
||||
|
||||
<Description>
|
||||
You can also try reloading the app to see if that solves the issue.
|
||||
{isNative() ? " Check the app store for an update, too." : ""}
|
||||
</Description>
|
||||
<IonButton onClick={unloadServiceWorkerAndRefresh}>Reload app</IonButton>
|
||||
|
||||
<hr />
|
||||
|
||||
<Description>
|
||||
If this crash is affecting many people, you can probably learn more{" "}
|
||||
<a
|
||||
href="https://lemmy.world/c/voyagerapp"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
at Voyager's Lemmy community
|
||||
</a>
|
||||
.
|
||||
</Description>
|
||||
|
||||
<Description>As a last resort, try clearing all app data.</Description>
|
||||
<IonButton color="danger" onClick={clearData}>
|
||||
Clear app data
|
||||
</IonButton>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
function generateCrashUrl(crashData: string): string {
|
||||
return `https://github.com/aeharding/voyager/issues/new?title=Crash&body=${encodeURIComponent(
|
||||
crashData,
|
||||
)}`;
|
||||
}
|
||||
|
||||
// The GitHub GET endpoint for opening a new issue
|
||||
// has a restriction for maximum length of a URL: 8192 bytes
|
||||
// https://github.com/cli/cli/pull/3271
|
||||
// https://github.com/cli/cli/issues/1575
|
||||
// https://github.com/cli/cli/blob/trunk/pkg/cmd/issue/create/create.go#L167
|
||||
// https://github.com/cli/cli/blob/trunk/utils/utils.go#L84
|
||||
const maxIssueBytes = 8150;
|
||||
|
||||
function getStrByteLength(str: string): number {
|
||||
return new TextEncoder().encode(str).length;
|
||||
}
|
||||
|
||||
function generateTruncatedCrashUrl(crashData: string): string {
|
||||
let url: string;
|
||||
let strLength = 1;
|
||||
|
||||
do {
|
||||
url = generateCrashUrl(crashData.slice(0, strLength));
|
||||
if (strLength === crashData.length) return url;
|
||||
strLength++;
|
||||
} while (getStrByteLength(url) < maxIssueBytes);
|
||||
|
||||
return url;
|
||||
}
|
Reference in New Issue
Block a user