forked from revenge-mod/revenge-bundle
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
587 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import ErrorBoundaryScreen from "@core/ui/reporter/components/ErrorBoundaryScreen"; | ||
import { after } from "@lib/api/patcher"; | ||
import { _lazyContextSymbol } from "@metro/lazy"; | ||
import { LazyModuleContext } from "@metro/types"; | ||
import { findByNameLazy } from "@metro/wrappers"; | ||
|
||
function getErrorBoundaryContext() { | ||
const ctxt: LazyModuleContext = findByNameLazy("ErrorBoundary")[_lazyContextSymbol]; | ||
return new Promise(resolve => ctxt.getExports(exp => resolve(exp.prototype))); | ||
} | ||
|
||
export default function patchErrorBoundary() { | ||
return after.await("render", getErrorBoundaryContext(), function (this: any) { | ||
if (!this.state.error) return; | ||
|
||
return <ErrorBoundaryScreen | ||
error={this.state.error} | ||
rerender={() => this.setState({ info: null, error: null })} | ||
/>; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { hasStack, isComponentStack } from "@core/ui/reporter/utils/isStack"; | ||
import { getDebugInfo, toggleSafeMode } from "@lib/api/debug"; | ||
import { BundleUpdaterManager } from "@lib/api/native/modules"; | ||
import { settings } from "@lib/api/settings"; | ||
import { Codeblock, ErrorBoundary } from "@lib/ui/components"; | ||
import { createStyles } from "@lib/ui/styles"; | ||
import { tokens } from "@metro/common"; | ||
import { Button, Card, SafeAreaView, Text } from "@metro/common/components"; | ||
import { ScrollView, View } from "react-native"; | ||
|
||
import ErrorComponentStackCard from "./ErrorComponentStackCard"; | ||
import ErrorStackCard from "./ErrorStackCard"; | ||
|
||
const useStyles = createStyles({ | ||
container: { | ||
flex: 1, | ||
backgroundColor: tokens.colors.BG_BASE_SECONDARY, | ||
paddingHorizontal: 16, | ||
height: "100%", | ||
padding: 8, | ||
gap: 12 | ||
} | ||
}); | ||
|
||
|
||
export default function ErrorBoundaryScreen(props: { | ||
error: Error; | ||
rerender: () => void; | ||
}) { | ||
const styles = useStyles(); | ||
const debugInfo = getDebugInfo(); | ||
|
||
return <ErrorBoundary> | ||
<SafeAreaView style={styles.container}> | ||
<View style={{ gap: 4 }}> | ||
<Text variant="display-lg">Uh oh.</Text> | ||
<Text variant="text-md/normal">A crash occured while rendering a component. This could be caused by a plugin, Bunny or Discord itself.</Text> | ||
<Text variant="text-sm/normal" color="text-muted">{debugInfo.os.name}; {debugInfo.discord.build} ({debugInfo.discord.version}); {debugInfo.bunny.version}</Text> | ||
</View> | ||
<ScrollView fadingEdgeLength={64} contentContainerStyle={{ gap: 12 }}> | ||
<Codeblock selectable={true}>{props.error.message}</Codeblock> | ||
{hasStack(props.error) && <ErrorStackCard error={props.error} />} | ||
{isComponentStack(props.error) ? <ErrorComponentStackCard componentStack={props.error.componentStack} /> : null} | ||
</ScrollView> | ||
<Card style={{ gap: 6 }}> | ||
<Button text="Reload Discord" onPress={() => BundleUpdaterManager.reload()} /> | ||
{!settings.safeMode?.enabled && <Button text="Reload in Safe Mode" onPress={() => toggleSafeMode()} />} | ||
<Button variant="destructive" text="Retry Render" onPress={() => props.rerender()} /> | ||
</Card> | ||
</SafeAreaView> | ||
</ErrorBoundary>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Strings } from "@core/i18n"; | ||
import { Codeblock } from "@lib/ui/components"; | ||
import { showSheet } from "@lib/ui/sheets"; | ||
import { Button, Card, Stack, Text, TwinButtons } from "@metro/common/components"; | ||
import { ReactNode } from "react"; | ||
|
||
import ErrorDetailsActionSheet from "./ErrorDetailsActionSheet"; | ||
|
||
export const INDEX_BUNDLE_FILE: string = window.HermesInternal.getFunctionLocation(window.__r).fileName; | ||
|
||
interface ErrorCardProps { | ||
error: unknown; | ||
header?: string | ReactNode; | ||
onRetryRender?: () => void; | ||
} | ||
|
||
export default function ErrorCard(props: ErrorCardProps) { | ||
return <Card> | ||
<Stack> | ||
{props.header && typeof props.header !== "string" | ||
? props.header | ||
: <Text variant="heading-lg/bold">{props.header ?? Strings.UH_OH}</Text> | ||
} | ||
<Codeblock selectable={true}>{String(props.error)}</Codeblock> | ||
<TwinButtons> | ||
{props.onRetryRender && <Button | ||
variant="destructive" | ||
// icon={findAssetId("RetryIcon")} | ||
text={Strings.RETRY_RENDER} | ||
onPress={props.onRetryRender} | ||
/>} | ||
{props.error instanceof Error ? <Button | ||
text={"Details"} | ||
// icon={findAssetId("CircleInformationIcon-primary")} | ||
onPress={() => showSheet( | ||
"BunnyErrorDetailsActionSheet", | ||
ErrorDetailsActionSheet, | ||
{ error: props.error as Error } | ||
)} | ||
/> : null} | ||
</TwinButtons> | ||
</Stack> | ||
</Card>; | ||
} |
53 changes: 53 additions & 0 deletions
53
src/core/ui/reporter/components/ErrorComponentStackCard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { parseComponentStack } from "@core/ui/reporter/utils/parseComponentStack"; | ||
import { findAssetId } from "@lib/api/assets"; | ||
import { clipboard } from "@metro/common"; | ||
import { Button, Card, Text } from "@metro/common/components"; | ||
import { useState } from "react"; | ||
import { Image, View } from "react-native"; | ||
|
||
export default function ErrorComponentStackCard(props: { | ||
componentStack: string; | ||
}) { | ||
const [collapsed, setCollapsed] = useState(true); | ||
|
||
let stack: string[]; | ||
try { | ||
stack = parseComponentStack(props.componentStack); | ||
stack = collapsed ? stack.slice(0, 4) : stack; | ||
} catch { | ||
return; | ||
} | ||
|
||
return <Card> | ||
<View style={{ gap: 8 }}> | ||
<Text variant="heading-lg/bold"> | ||
Component Stack | ||
</Text> | ||
<View style={{ gap: 4 }}> | ||
{stack.map(component => ( | ||
<View style={{ flexDirection: "row" }}> | ||
<Text variant="text-md/bold" color="text-muted">{"<"}</Text> | ||
<Text variant="text-md/bold">{component}</Text> | ||
<Text variant="text-md/bold" color="text-muted">{"/>"}</Text> | ||
</View> | ||
))} | ||
</View> | ||
{collapsed && <Text>...</Text>} | ||
<View style={{ gap: 8, flexDirection: "row", justifyContent: "center", alignItems: "center" }}> | ||
<Button | ||
variant="secondary" | ||
text={`Show ${collapsed ? "more" : "less"}`} | ||
icon={collapsed ? findAssetId("down_arrow") : <Image | ||
style={{ transform: [{ rotate: `${collapsed ? 0 : 180}deg` }] }} | ||
source={findAssetId("down_arrow")!} | ||
/>} | ||
onPress={() => setCollapsed(v => !v)} /> | ||
<Button | ||
variant="secondary" | ||
text="Copy" | ||
icon={findAssetId("CopyIcon")} | ||
onPress={() => clipboard.setString(props.componentStack)} /> | ||
</View> | ||
</View> | ||
</Card>; | ||
} |
20 changes: 20 additions & 0 deletions
20
src/core/ui/reporter/components/ErrorDetailsActionSheet.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { hasStack, isComponentStack } from "@core/ui/reporter/utils/isStack"; | ||
import { Codeblock } from "@lib/ui/components"; | ||
import { ActionSheet, Text } from "@metro/common/components"; | ||
import { View } from "react-native"; | ||
|
||
import ErrorComponentStackCard from "./ErrorComponentStackCard"; | ||
import ErrorStackCard from "./ErrorStackCard"; | ||
|
||
export default function ErrorDetailsActionSheet(props: { | ||
error: Error; | ||
}) { | ||
return <ActionSheet> | ||
<View style={{ gap: 12, paddingVertical: 12 }}> | ||
<Text variant="heading-lg/extrabold">Error</Text> | ||
<Codeblock selectable={true}>{props.error.message}</Codeblock> | ||
{hasStack(props.error) && <ErrorStackCard error={props.error} />} | ||
{isComponentStack(props.error) ? <ErrorComponentStackCard componentStack={props.error.componentStack} /> : null} | ||
</View> | ||
</ActionSheet>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import parseErrorStack, { StackFrame } from "@core/ui/reporter/utils/parseErrorStack"; | ||
import { findAssetId } from "@lib/api/assets"; | ||
import { clipboard, constants } from "@metro/common"; | ||
import { Button, Card, Text } from "@metro/common/components"; | ||
import { useState } from "react"; | ||
import { Image, Pressable, View } from "react-native"; | ||
|
||
import { INDEX_BUNDLE_FILE } from "./ErrorCard"; | ||
|
||
export default function ErrorStackCard(props: { | ||
error: Error & { stack: string }; | ||
}) { | ||
const [collapsed, setCollapsed] = useState(true); | ||
|
||
let stack: StackFrame[]; | ||
|
||
try { | ||
const parsedErrorStack = parseErrorStack(props.error.stack); | ||
stack = collapsed ? parsedErrorStack.slice(0, 4) : parsedErrorStack; | ||
} catch { | ||
return null; | ||
} | ||
|
||
return <Card> | ||
<View style={{ gap: 12 }}> | ||
<Text variant="heading-lg/bold"> | ||
Call Stack | ||
</Text> | ||
<View style={{ gap: 4 }}> | ||
{stack.map((f, id) => <Line id={id} frame={f} />)} | ||
</View> | ||
{collapsed && <Text>...</Text>} | ||
<View style={{ gap: 8, flexDirection: "row", justifyContent: "center", alignItems: "center" }}> | ||
<Button | ||
variant="secondary" | ||
text={`Show ${collapsed ? "more" : "less"}`} | ||
icon={collapsed ? findAssetId("down_arrow") : <Image | ||
style={{ transform: [{ rotate: `${collapsed ? 0 : 180}deg` }] }} | ||
source={findAssetId("down_arrow")!} | ||
/>} | ||
onPress={() => setCollapsed(v => !v)} /> | ||
<Button | ||
variant="secondary" | ||
text="Copy" | ||
icon={findAssetId("CopyIcon")} | ||
onPress={() => clipboard.setString(props.error.stack)} /> | ||
</View> | ||
</View> | ||
</Card>; | ||
} | ||
function Line(props: { id: number, frame: StackFrame }) { | ||
const [collapsed, setCollapsed] = useState(true); | ||
|
||
return <Pressable onPress={() => setCollapsed(v => !v)} key={props.id}> | ||
<Text style={{ fontFamily: constants.Fonts.CODE_BOLD }}> | ||
{props.frame.methodName} | ||
</Text> | ||
<Text style={{ fontFamily: constants.Fonts.CODE_NORMAL }} ellipsizeMode="middle" numberOfLines={collapsed ? 1 : undefined}> | ||
<Text color="text-muted">{props.frame.file === INDEX_BUNDLE_FILE ? "jsbundle" : props.frame.file}:{props.frame.lineNumber}:{props.frame.column}</Text> | ||
</Text> | ||
</Pressable>; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export function isComponentStack(error: Error): error is Error & { componentStack: string; } { | ||
return "componentStack" in error && typeof error.componentStack === "string"; | ||
} | ||
export function hasStack(error: Error): error is Error & { stack: string; } { | ||
return !!error.stack; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function parseComponentStack(componentStack: string) { | ||
return componentStack.split(/[\s|\n]+?in /).filter(Boolean); | ||
} |
Oops, something went wrong.