Update developer options and ErrorBoundary
Developer options: - Remove option to trip ShiggyCord ErrorBoundary (broken) ErrorBoundary: - Rewrite bottom buttons - Remove core plugins section
This commit is contained in:
parent
35bcfb4ac8
commit
f4e9d86735
2 changed files with 76 additions and 168 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { hasStack, isComponentStack } from "@core/ui/reporter/utils/isStack";
|
import { hasStack, isComponentStack } from "@core/ui/reporter/utils/isStack";
|
||||||
|
import parseErrorStack from "@core/ui/reporter/utils/parseErrorStack";
|
||||||
import { getDebugInfo, toggleSafeMode } from "@lib/api/debug";
|
import { getDebugInfo, toggleSafeMode } from "@lib/api/debug";
|
||||||
import { BundleUpdaterManager } from "@lib/api/native/modules";
|
import { BundleUpdaterManager } from "@lib/api/native/modules";
|
||||||
import { settings } from "@lib/api/settings";
|
import { settings } from "@lib/api/settings";
|
||||||
|
|
@ -9,201 +10,119 @@ import { tokens } from "@metro/common";
|
||||||
import { showToast } from "@lib/ui/toasts";
|
import { showToast } from "@lib/ui/toasts";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
SafeAreaProvider,
|
SafeAreaProvider,
|
||||||
SafeAreaView,
|
SafeAreaView,
|
||||||
Text,
|
Text,
|
||||||
TableRowGroup,
|
TableRowGroup,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
TableSwitchRow,
|
||||||
|
Stack,
|
||||||
} from "@metro/common/components";
|
} from "@metro/common/components";
|
||||||
import { ScrollView, View, Clipboard } from "react-native";
|
import { ScrollView, View } from "react-native";
|
||||||
|
|
||||||
import ErrorComponentStackCard from "./ErrorComponentStackCard";
|
import ErrorComponentStackCard from "./ErrorComponentStackCard";
|
||||||
import ErrorStackCard from "./ErrorStackCard";
|
import ErrorStackCard from "./ErrorStackCard";
|
||||||
|
import { NavigationNative } from "@metro/common";
|
||||||
|
|
||||||
const useStyles = createStyles(() => ({
|
const useStyles = createStyles({
|
||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: tokens.colors.BG_BASE_SECONDARY,
|
backgroundColor: tokens.colors.BG_BASE_SECONDARY,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
height: "100%",
|
||||||
|
gap: 12,
|
||||||
},
|
},
|
||||||
scrollContent: {
|
});
|
||||||
paddingBottom: 100, // Extra space for fixed buttons
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
alignItems: "center",
|
|
||||||
paddingVertical: 32,
|
|
||||||
backgroundColor: tokens.colors.BG_BASE_SECONDARY,
|
|
||||||
},
|
|
||||||
headerText: {
|
|
||||||
textAlign: "center",
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
debugInfo: {
|
|
||||||
fontSize: 12,
|
|
||||||
color: tokens.colors.TEXT_MUTED,
|
|
||||||
textAlign: "center",
|
|
||||||
marginTop: 8,
|
|
||||||
},
|
|
||||||
section: {
|
|
||||||
marginHorizontal: 16,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
fixedButtonContainer: {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
backgroundColor: tokens.colors.BG_MOD_STRONG,
|
|
||||||
padding: 16,
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
buttonRow: {
|
|
||||||
flexDirection: "row",
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
fullButton: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
errorMessageRow: {
|
|
||||||
backgroundColor: tokens.colors.BG_MOD_FAINT,
|
|
||||||
borderRadius: 8,
|
|
||||||
marginVertical: 4,
|
|
||||||
overflow: "hidden",
|
|
||||||
},
|
|
||||||
centeredErrorText: {
|
|
||||||
color: tokens.colors.TEXT_DANGER,
|
|
||||||
fontSize: 14,
|
|
||||||
textAlign: "center",
|
|
||||||
paddingVertical: 8,
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
// Component entrypoint
|
||||||
export default function ErrorBoundaryScreen(props: {
|
export default function ErrorBoundaryScreen(props: {
|
||||||
error: Error;
|
error: Error;
|
||||||
rerender: () => void;
|
rerender: () => void;
|
||||||
}) {
|
}) {
|
||||||
|
// Hooks MUST be at the top level and unconditionally called.
|
||||||
const styles = useStyles();
|
const styles = useStyles();
|
||||||
const debugInfo = getDebugInfo();
|
const debugInfo = getDebugInfo();
|
||||||
|
const navigation = NavigationNative.useNavigation();
|
||||||
|
|
||||||
const copyErrorDetails = () => {
|
// Bisect UI state (fixes ReferenceError when referenced below)
|
||||||
const errorText = `Error: ${props.error.message}\n\nStack: ${props.error.stack || "No stack trace"}`;
|
const [bisectBatches, setBisectBatches] = React.useState<string[][] | null>(
|
||||||
Clipboard.setString(errorText);
|
null,
|
||||||
showToast("Error details copied");
|
);
|
||||||
};
|
const [bisectIndex, setBisectIndex] = React.useState<number>(0);
|
||||||
|
const [showBisectUI, setShowBisectUI] = React.useState<boolean>(false);
|
||||||
|
|
||||||
const copyStackTrace = () => {
|
// Use a safe fallback so renders don't throw if state hasn't been initialized yet
|
||||||
if (props.error.stack) {
|
const batches = bisectBatches ?? [];
|
||||||
Clipboard.setString(props.error.stack);
|
const currentBatch = batches[bisectIndex] ?? [];
|
||||||
showToast("Stack trace copied");
|
|
||||||
} else {
|
|
||||||
showToast("No stack trace available");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<SafeAreaProvider>
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
{/* Scrollable Content */}
|
<View style={{ gap: 4 }}>
|
||||||
<ScrollView contentContainerStyle={styles.scrollContent}>
|
<Text variant="display-lg">Uh oh.</Text>
|
||||||
{/* Header - Using the same approach as About.tsx */}
|
<Text variant="text-md/normal">
|
||||||
<View style={styles.header}>
|
A crash occurred while rendering a component. This could be caused
|
||||||
<Text variant="heading-xl/bold" style={styles.headerText}>
|
by a plugin, ShiggyCord, or Discord itself.
|
||||||
Uh oh.
|
</Text>
|
||||||
</Text>
|
<Text variant="text-sm/normal" color="text-muted">
|
||||||
<Text variant="text-md/normal" style={styles.headerText}>
|
{debugInfo.os.name}; {debugInfo.discord.build} (
|
||||||
A crash occurred while rendering a component
|
{debugInfo.discord.version}); {debugInfo.bunny.version}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.debugInfo}>
|
</View>
|
||||||
{debugInfo.os.name} • Discord {debugInfo.discord.version} •{" "}
|
<ScrollView fadingEdgeLength={64} contentContainerStyle={{ gap: 12 }}>
|
||||||
{debugInfo.bunny.version}
|
<Text
|
||||||
</Text>
|
variant="heading-md/extrabold"
|
||||||
</View>
|
style={{
|
||||||
|
color: tokens.colors.TEXT_NORMAL,
|
||||||
|
marginBottom: 8,
|
||||||
|
textAlign: "center",
|
||||||
|
}}
|
||||||
|
selectable
|
||||||
|
>
|
||||||
|
{props.error.message || "Unknown error occurred"}
|
||||||
|
</Text>
|
||||||
|
|
||||||
{/* Error Details */}
|
{hasStack(props.error) && <ErrorStackCard error={props.error} />}
|
||||||
<View style={styles.section}>
|
{isComponentStack(props.error) ? (
|
||||||
<TableRowGroup title="Error Details">
|
<ErrorComponentStackCard
|
||||||
<View style={styles.errorMessageRow}>
|
componentStack={props.error.componentStack}
|
||||||
<TableRow
|
/>
|
||||||
label={
|
) : null}
|
||||||
<Text style={styles.centeredErrorText}>
|
|
||||||
{props.error.message || "Unknown error occurred"}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
onPress={copyErrorDetails}
|
|
||||||
style={{ justifyContent: "center" }}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</TableRowGroup>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
{/* Stack Traces */}
|
|
||||||
{hasStack(props.error) && (
|
|
||||||
<View style={styles.section}>
|
|
||||||
<TableRowGroup title="JavaScript Stack Trace">
|
|
||||||
<View style={{ paddingHorizontal: 8, paddingBottom: 8 }}>
|
|
||||||
<ErrorStackCard error={props.error} />
|
|
||||||
</View>
|
|
||||||
</TableRowGroup>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isComponentStack(props.error) && (
|
|
||||||
<View style={styles.section}>
|
|
||||||
<TableRowGroup title="Component Stack">
|
|
||||||
<View style={{ paddingHorizontal: 8, paddingBottom: 8 }}>
|
|
||||||
<ErrorComponentStackCard
|
|
||||||
componentStack={props.error.componentStack}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</TableRowGroup>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Quick Actions */}
|
|
||||||
<View style={styles.section}>
|
|
||||||
<TableRowGroup title="Quick Actions">
|
|
||||||
<TableRow label="Copy Error Message" onPress={copyErrorDetails} />
|
|
||||||
{props.error.stack && (
|
|
||||||
<TableRow label="Copy Stack Trace" onPress={copyStackTrace} />
|
|
||||||
)}
|
|
||||||
</TableRowGroup>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
<Card style={{ gap: 12, paddingVertical: 16, alignItems: "center" }}>
|
||||||
{/* Fixed Bottom Buttons - Always visible */}
|
<Stack
|
||||||
<View style={styles.fixedButtonContainer}>
|
direction="horizontal"
|
||||||
<View style={styles.buttonRow}>
|
spacing={12}
|
||||||
|
style={{ justifyContent: "center", width: "100%" }}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
text="Reload Discord"
|
text="Reload Discord"
|
||||||
style={styles.fullButton}
|
|
||||||
onPress={() => BundleUpdaterManager.reload()}
|
onPress={() => BundleUpdaterManager.reload()}
|
||||||
|
style={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
{!settings.safeMode?.enabled && (
|
{!settings.safeMode?.enabled && (
|
||||||
<Button
|
<Button
|
||||||
text="Safe Mode"
|
text="Safe Mode"
|
||||||
variant="secondary"
|
|
||||||
style={styles.fullButton}
|
|
||||||
onPress={() => toggleSafeMode()}
|
onPress={() => toggleSafeMode()}
|
||||||
|
style={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</Stack>
|
||||||
|
<Button
|
||||||
<View style={styles.buttonRow}>
|
variant="destructive"
|
||||||
<Button
|
text="Retry Render"
|
||||||
text="Retry Render"
|
onPress={() => {
|
||||||
variant="destructive"
|
try {
|
||||||
style={{ flex: 1 }}
|
console.log(
|
||||||
onPress={() => {
|
"[ShiggyCord][ErrorBoundaryScreen] Retry Render clicked",
|
||||||
try {
|
);
|
||||||
console.log(
|
} catch {}
|
||||||
"[ShiggyCord][ErrorBoundaryScreen] Retry Render clicked",
|
props.rerender();
|
||||||
);
|
}}
|
||||||
} catch {}
|
style={{ width: "100%" }}
|
||||||
props.rerender();
|
/>
|
||||||
}}
|
</Card>
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -280,18 +280,7 @@ export default function Developer() {
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: Strings.SHIGGYCORD,
|
label: "ShiggyCord",
|
||||||
onPress: () =>
|
|
||||||
navigation.push("SHIGGYCORD_CUSTOM_PAGE", {
|
|
||||||
render: () => {
|
|
||||||
// Trigger an error by accessing a property on undefined
|
|
||||||
const undefinedObj: any = undefined;
|
|
||||||
return undefinedObj.someProperty;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Discord",
|
|
||||||
isDestructive: true,
|
isDestructive: true,
|
||||||
onPress: () =>
|
onPress: () =>
|
||||||
navigation.push("SHIGGYCORD_CUSTOM_PAGE", {
|
navigation.push("SHIGGYCORD_CUSTOM_PAGE", {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue