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
// (`` is at the root of the document tree)
const location = memoryHistory ? memoryHistory.location : window.location;
const loggedIn = loggedInSelector(store.getState());
let crashData = `
### Crash description
### 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 (
🫣 Gah! Voyager crashed!
Voyager does not collect any data, so we would appreciate you
voluntarily submitting this crash for us to investigate.
Open Github issue with crash data
You can also try reloading the app to see if that solves the issue.
{isNative() ? " Check the app store for an update, too." : ""}
Reload app
If this crash is affecting many people, you can probably learn more{" "}
at Voyager's Lemmy community
.
As a last resort, try clearing all app data.
Clear app data
);
}
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;
}