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:
kmmiio99o 2025-12-07 17:14:06 +01:00
parent 35bcfb4ac8
commit f4e9d86735
2 changed files with 76 additions and 168 deletions

View file

@ -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>
); );

View file

@ -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", {