[Lib > Storage] Store and throw errors
+ Added some debug infos on Discord's error boundary
This commit is contained in:
parent
98cecb4b6a
commit
eed535549e
2 changed files with 24 additions and 6 deletions
|
@ -3,6 +3,7 @@ import createEmitter from "@lib/emitter";
|
||||||
|
|
||||||
const emitterSymbol = Symbol.for("vendetta.storage.emitter");
|
const emitterSymbol = Symbol.for("vendetta.storage.emitter");
|
||||||
const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor");
|
const syncAwaitSymbol = Symbol.for("vendetta.storage.accessor");
|
||||||
|
const storageErrorSymbol = Symbol.for("vendetta.storage.error");
|
||||||
|
|
||||||
export function createProxy(target: any = {}): { proxy: any; emitter: Emitter } {
|
export function createProxy(target: any = {}): { proxy: any; emitter: Emitter } {
|
||||||
const emitter = createEmitter();
|
const emitter = createEmitter();
|
||||||
|
@ -56,8 +57,12 @@ export function createProxy(target: any = {}): { proxy: any; emitter: Emitter }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useProxy<T>(storage: T): T {
|
export function useProxy<T>(storage: T & { [key: symbol]: any }): T {
|
||||||
const emitter = (storage as any)[emitterSymbol] as Emitter;
|
if (storage[storageErrorSymbol]) throw storage[storageErrorSymbol];
|
||||||
|
|
||||||
|
const emitter = storage[emitterSymbol] as Emitter;
|
||||||
|
|
||||||
|
if (!emitter) throw new Error("InvalidArgumentExcpetion - storage[emitterSymbol] is " + typeof emitter);
|
||||||
|
|
||||||
const [, forceUpdate] = React.useReducer((n) => ~n, 0);
|
const [, forceUpdate] = React.useReducer((n) => ~n, 0);
|
||||||
|
|
||||||
|
@ -89,6 +94,7 @@ export async function createStorage<T>(backend: StorageBackend): Promise<Awaited
|
||||||
|
|
||||||
export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
let awaited: any = undefined;
|
let awaited: any = undefined;
|
||||||
|
let error: any = undefined;
|
||||||
|
|
||||||
const awaitQueue: (() => void)[] = [];
|
const awaitQueue: (() => void)[] = [];
|
||||||
const awaitInit = (cb: () => void) => (awaited ? cb() : awaitQueue.push(cb));
|
const awaitInit = (cb: () => void) => (awaited ? cb() : awaitQueue.push(cb));
|
||||||
|
@ -96,6 +102,8 @@ export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
store.then((v) => {
|
store.then((v) => {
|
||||||
awaited = v;
|
awaited = v;
|
||||||
awaitQueue.forEach((cb) => cb());
|
awaitQueue.forEach((cb) => cb());
|
||||||
|
}).catch((e) => {
|
||||||
|
error = e;
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Proxy({} as Awaited<T>, {
|
return new Proxy({} as Awaited<T>, {
|
||||||
|
@ -105,6 +113,7 @@ export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
.map((k) => [k, (t: T, ...a: any[]) => Reflect[k](awaited ?? t, ...a)])
|
.map((k) => [k, (t: T, ...a: any[]) => Reflect[k](awaited ?? t, ...a)])
|
||||||
),
|
),
|
||||||
get(target, prop, recv) {
|
get(target, prop, recv) {
|
||||||
|
if (prop === storageErrorSymbol) return error;
|
||||||
if (prop === syncAwaitSymbol) return awaitInit;
|
if (prop === syncAwaitSymbol) return awaitInit;
|
||||||
return Reflect.get(awaited ?? target, prop, recv);
|
return Reflect.get(awaited ?? target, prop, recv);
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { ButtonColors } from "@types";
|
||||||
import { ReactNative as RN, stylesheet } from "@metro/common";
|
import { ReactNative as RN, stylesheet } from "@metro/common";
|
||||||
import { findByName, findByProps, findByStoreName } from "@metro/filters";
|
import { findByName, findByProps, findByStoreName } from "@metro/filters";
|
||||||
import { after } from "@lib/patcher";
|
import { after } from "@lib/patcher";
|
||||||
import { toggleSafeMode } from "@lib/debug";
|
import { getDebugInfo, toggleSafeMode } from "@lib/debug";
|
||||||
import { DeviceManager } from "@lib/native";
|
import { DeviceManager } from "@lib/native";
|
||||||
import { semanticColors } from "@ui/color";
|
import { semanticColors } from "@ui/color";
|
||||||
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
|
import { Button, Codeblock, ErrorBoundary as _ErrorBoundary, SafeAreaView } from "@ui/components";
|
||||||
|
@ -13,8 +13,6 @@ const ErrorBoundary = findByName("ErrorBoundary");
|
||||||
// Let's just pray they have this.
|
// Let's just pray they have this.
|
||||||
const { BadgableTabBar } = findByProps("BadgableTabBar");
|
const { BadgableTabBar } = findByProps("BadgableTabBar");
|
||||||
|
|
||||||
const ThemeStore = findByStoreName("ThemeStore");
|
|
||||||
|
|
||||||
const { TextStyleSheet } = findByProps("TextStyleSheet");
|
const { TextStyleSheet } = findByProps("TextStyleSheet");
|
||||||
const styles = stylesheet.createThemedStyleSheet({
|
const styles = stylesheet.createThemedStyleSheet({
|
||||||
container: {
|
container: {
|
||||||
|
@ -70,6 +68,8 @@ const tabs: Tab[] = [
|
||||||
export default () => after("render", ErrorBoundary.prototype, function (this: any, _, ret) {
|
export default () => after("render", ErrorBoundary.prototype, function (this: any, _, ret) {
|
||||||
if (!this.state.error) return;
|
if (!this.state.error) return;
|
||||||
|
|
||||||
|
const debugInfo = getDebugInfo();
|
||||||
|
|
||||||
// Not using setState here as we don't want to cause a re-render, we want this to be set in the initial render
|
// Not using setState here as we don't want to cause a re-render, we want this to be set in the initial render
|
||||||
this.state.activeTab ??= "message";
|
this.state.activeTab ??= "message";
|
||||||
const tabData = tabs.find(t => t.id === this.state.activeTab);
|
const tabData = tabs.find(t => t.id === this.state.activeTab);
|
||||||
|
@ -86,7 +86,7 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
||||||
<_ErrorBoundary>
|
<_ErrorBoundary>
|
||||||
<SafeAreaView style={styles.container}>
|
<SafeAreaView style={styles.container}>
|
||||||
<RN.View style={styles.header}>
|
<RN.View style={styles.header}>
|
||||||
<RN.Image style={{ flex: 1, resizeMode: "contain", maxHeight: 96, paddingRight: 4 }} source={ThemeStore.theme === "light" ? ret.props.lightSource : ret.props.darkSource} />
|
{ret.props.Illustration && <ret.props.Illustration style={{ flex: 1, resizeMode: "contain", maxHeight: 96, paddingRight: 4 }} /> }
|
||||||
<RN.View style={{ flex: 2, paddingLeft: 4 }}>
|
<RN.View style={{ flex: 2, paddingLeft: 4 }}>
|
||||||
<RN.Text style={styles.headerTitle}>{ret.props.title}</RN.Text>
|
<RN.Text style={styles.headerTitle}>{ret.props.title}</RN.Text>
|
||||||
<RN.Text style={styles.headerDescription}>{ret.props.body}</RN.Text>
|
<RN.Text style={styles.headerDescription}>{ret.props.body}</RN.Text>
|
||||||
|
@ -101,6 +101,15 @@ export default () => after("render", ErrorBoundary.prototype, function (this: an
|
||||||
onTabSelected={(tab: string) => { this.setState({ activeTab: tab }) }}
|
onTabSelected={(tab: string) => { this.setState({ activeTab: tab }) }}
|
||||||
/>
|
/>
|
||||||
</RN.View>
|
</RN.View>
|
||||||
|
<Codeblock
|
||||||
|
selectable
|
||||||
|
style={{ flexBasis: "auto", marginBottom: 8 }}
|
||||||
|
>
|
||||||
|
{[
|
||||||
|
`Discord: ${debugInfo.discord.build} (${debugInfo.os.name})`,
|
||||||
|
`Vendetta: ${debugInfo.vendetta.version}`
|
||||||
|
].join("\n")}
|
||||||
|
</Codeblock>
|
||||||
<Codeblock
|
<Codeblock
|
||||||
selectable
|
selectable
|
||||||
style={{ flex: 1, textAlignVertical: "top" }}
|
style={{ flex: 1, textAlignVertical: "top" }}
|
||||||
|
|
Loading…
Reference in a new issue