From a20735466dbb95cc7c5f9575505c32a5c0bf84fb Mon Sep 17 00:00:00 2001 From: pylixonly <82711525+pylixonly@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:20:44 +0800 Subject: [PATCH 1/7] refactor(ui): update styles & ErrorBoundary - Remove unused import of createThemedStyleSheet from ErrorBoundary component - Remove unneccesary import of ErrorBoundary from safeMode.tsx - Update createStyles function to use Styles.createStyles instead of CompatfulRedesign.createStyles - Add createLegacyClassComponentStyles function to create themed styles (context-ly) for class components - Deprecated createThemedStyleSheet function --- src/lib/ui/components/ErrorBoundary.tsx | 6 ++- src/lib/ui/safeMode.tsx | 2 - src/lib/ui/styles.ts | 52 ++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/lib/ui/components/ErrorBoundary.tsx b/src/lib/ui/components/ErrorBoundary.tsx index 8a5b073..e6799bc 100644 --- a/src/lib/ui/components/ErrorBoundary.tsx +++ b/src/lib/ui/components/ErrorBoundary.tsx @@ -1,8 +1,8 @@ import { Strings } from "@core/i18n"; +import { createLegacyClassComponentStyles, ThemeContext } from "@lib/ui/styles"; import { React } from "@metro/common"; import { Button, LegacyFormText } from "@metro/common/components"; import { Codeblock } from "@ui/components"; -import { createThemedStyleSheet } from "@ui/styles"; import { Falsy, ScrollView } from "react-native"; type ErrorBoundaryState = { @@ -16,7 +16,7 @@ export interface ErrorBoundaryProps { children: JSX.Element | Falsy | (JSX.Element | Falsy)[]; } -const styles = createThemedStyleSheet({ +const getStyles = createLegacyClassComponentStyles({ view: { flex: 1, flexDirection: "column", @@ -35,9 +35,11 @@ export default class ErrorBoundary extends React.Component ({ hasErr: true, error }); render() { + const styles = getStyles(this.context); if (!this.state.hasErr) return this.props.children; return ( diff --git a/src/lib/ui/safeMode.tsx b/src/lib/ui/safeMode.tsx index a3c04a7..d8c0cd2 100644 --- a/src/lib/ui/safeMode.tsx +++ b/src/lib/ui/safeMode.tsx @@ -13,8 +13,6 @@ import { Codeblock, ErrorBoundary as _ErrorBoundary } from "@ui/components"; import { createThemedStyleSheet, TextStyleSheet } from "@ui/styles"; import { Text, View } from "react-native"; -const ErrorBoundary = findByNameLazy("ErrorBoundary"); - // Let's just pray they have this. const { BadgableTabBar } = lazyDestructure(() => findByProps("BadgableTabBar")); diff --git a/src/lib/ui/styles.ts b/src/lib/ui/styles.ts index c3bb9e5..2c5d7de 100644 --- a/src/lib/ui/styles.ts +++ b/src/lib/ui/styles.ts @@ -6,18 +6,60 @@ import { ImageStyle, StyleSheet, TextStyle, ViewStyle } from "react-native"; type NamedStyles = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle }; -const CompatfulRedesign = findByPropsLazy("createStyles"); +const Styles = findByPropsLazy("createStyles"); +export const { ThemeContext } = lazyDestructure(() => findByProps("ThemeContext"), { hint: "object" }); export const { TextStyleSheet } = lazyDestructure(() => findByProps("TextStyleSheet")) as unknown as { - TextStyleSheet: { [key in DiscordTextStyles]: TextStyle; }; + TextStyleSheet: Record; }; +/** + * Get themed styles based on the current theme + * @returns A hook that returns the themed stylesheet + * @example + * const useStyles = createStyles({ + * container: { + * flex: 1, + * backgroundColor: tokens.colors.BACKGROUND_PRIMARY, + * }, + * }); + * + * function MyComponent() { + * const styles = useStyles(); + * return ; + * } + */ export function createStyles>(sheet: T | ((props: any) => T)): () => T { - return proxyLazy(() => CompatfulRedesign.createStyles(sheet)); + return proxyLazy(() => Styles.createStyles(sheet)); } -// Reimplementation of Discord's createThemedStyleSheet, which was removed since 204201 -// Not exactly a 1:1 reimplementation, but sufficient to keep compatibility with existing plugins +/** + * Get themed styles based on the current theme for class components + * @example + * const getStyles = createStyles({ + * container: { + * flex: 1, + * backgroundColor: tokens.colors.BACKGROUND_PRIMARY, + * }, + * }); + * + * class MyComponent extends React.Component { + * static contextType = ThemeContext; + * render() { + * const styles = getStyles(this.context); + * return ; + * } + * } + */ +export function createLegacyClassComponentStyles>(sheet: T | ((props: any) => T)): (ctxt: typeof ThemeContext) => T { + return proxyLazy(() => Styles.createLegacyClassComponentStyles(sheet)); +} + +/** + * Reimplementation of Discord's createThemedStyleSheet, which was removed since 204201 + * Not exactly a 1:1 reimplementation, but sufficient to keep compatibility with existing plugins + * @deprecated Use createStyles or createLegacyClassComponentStyles instead +*/ export function createThemedStyleSheet>(sheet: T) { for (const key in sheet) { // @ts-ignore From f44791049a762fbce777b03e9d930fba0c0c6570 Mon Sep 17 00:00:00 2001 From: pylixonly <82711525+pylixonly@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:37:07 +0800 Subject: [PATCH 2/7] feat: port ErrorBoundary from dev --- src/core/debug/patches/patchErrorBoundary.tsx | 21 ++ .../components/ErrorBoundaryScreen.tsx | 52 +++++ src/core/ui/reporter/components/ErrorCard.tsx | 44 ++++ .../components/ErrorComponentStackCard.tsx | 53 +++++ .../components/ErrorDetailsActionSheet.tsx | 20 ++ .../ui/reporter/components/ErrorStackCard.tsx | 63 ++++++ src/core/ui/reporter/utils/isStack.tsx | 6 + .../ui/reporter/utils/parseComponentStack.tsx | 3 + src/core/ui/reporter/utils/parseErrorStack.ts | 202 ++++++++++++++++++ src/index.ts | 4 +- src/lib/ui/components/ErrorBoundary.tsx | 40 +--- src/lib/ui/safeMode.tsx | 149 ------------- src/lib/ui/sheets.ts | 3 +- 13 files changed, 475 insertions(+), 185 deletions(-) create mode 100644 src/core/debug/patches/patchErrorBoundary.tsx create mode 100644 src/core/ui/reporter/components/ErrorBoundaryScreen.tsx create mode 100644 src/core/ui/reporter/components/ErrorCard.tsx create mode 100644 src/core/ui/reporter/components/ErrorComponentStackCard.tsx create mode 100644 src/core/ui/reporter/components/ErrorDetailsActionSheet.tsx create mode 100644 src/core/ui/reporter/components/ErrorStackCard.tsx create mode 100644 src/core/ui/reporter/utils/isStack.tsx create mode 100644 src/core/ui/reporter/utils/parseComponentStack.tsx create mode 100644 src/core/ui/reporter/utils/parseErrorStack.ts delete mode 100644 src/lib/ui/safeMode.tsx diff --git a/src/core/debug/patches/patchErrorBoundary.tsx b/src/core/debug/patches/patchErrorBoundary.tsx new file mode 100644 index 0000000..ccab32a --- /dev/null +++ b/src/core/debug/patches/patchErrorBoundary.tsx @@ -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 this.setState({ info: null, error: null })} + />; + }); +} diff --git a/src/core/ui/reporter/components/ErrorBoundaryScreen.tsx b/src/core/ui/reporter/components/ErrorBoundaryScreen.tsx new file mode 100644 index 0000000..b6beffe --- /dev/null +++ b/src/core/ui/reporter/components/ErrorBoundaryScreen.tsx @@ -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.BACKGROUND_PRIMARY, + paddingHorizontal: 16, + height: "100%", + padding: 8, + gap: 12 + } +}); + + +export default function ErrorBoundaryScreen(props: { + error: Error; + rerender: () => void; +}) { + const styles = useStyles(); + const debugInfo = getDebugInfo(); + + return + + + Uh oh. + A crash occured while rendering a component. This could be caused by a plugin, Bunny or Discord itself. + {debugInfo.os.name}; {debugInfo.discord.build} ({debugInfo.discord.version}); {debugInfo.bunny.version} + + + {props.error.message} + {hasStack(props.error) && } + {isComponentStack(props.error) ? : null} + + +