fix: modern Discord themes compatibility, storage async resilience, and startup safety
This commit is contained in:
parent
1547421074
commit
ad7d405b7c
6 changed files with 199 additions and 123 deletions
|
|
@ -13,8 +13,9 @@ export function createProxy(target: any = {}): { proxy: any; emitter: Emitter; }
|
||||||
|
|
||||||
function createProxy(target: any, path: string[]): any {
|
function createProxy(target: any, path: string[]): any {
|
||||||
return new Proxy(target, {
|
return new Proxy(target, {
|
||||||
get(target, prop: string) {
|
get(target, prop: string | symbol) {
|
||||||
if ((prop as unknown) === emitterSymbol) return emitter;
|
if ((prop as unknown) === emitterSymbol) return emitter;
|
||||||
|
if (typeof prop === "symbol") return target[prop];
|
||||||
|
|
||||||
const newPath = [...path, prop];
|
const newPath = [...path, prop];
|
||||||
const value: any = target[prop];
|
const value: any = target[prop];
|
||||||
|
|
@ -82,25 +83,45 @@ export function createProxy(target: any = {}): { proxy: any; emitter: Emitter; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useProxy<T>(storage: T): T {
|
export function useProxy<T>(storage: T): T {
|
||||||
const emitter = (storage as any)?.[emitterSymbol] as Emitter;
|
|
||||||
if (!emitter) throw new Error("storage?.[emitterSymbol] is undefined");
|
|
||||||
|
|
||||||
const [, forceUpdate] = React.useReducer(n => ~n, 0);
|
const [, forceUpdate] = React.useReducer(n => ~n, 0);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const listener: EmitterListener = (event: EmitterEvent, data: EmitterListenerData) => {
|
if (!storage) return;
|
||||||
if (event === "DEL" && data.value === storage) return;
|
|
||||||
forceUpdate();
|
let unsubscribe: (() => void) | undefined;
|
||||||
|
|
||||||
|
const attach = (s: any) => {
|
||||||
|
const emitter = s?.[emitterSymbol] as Emitter | undefined;
|
||||||
|
if (!emitter) return;
|
||||||
|
|
||||||
|
const listener: EmitterListener = (event: EmitterEvent, data: EmitterListenerData) => {
|
||||||
|
if (event === "DEL" && data.value === s) return;
|
||||||
|
forceUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
emitter.on("SET", listener);
|
||||||
|
emitter.on("DEL", listener);
|
||||||
|
|
||||||
|
unsubscribe = () => {
|
||||||
|
emitter.off("SET", listener);
|
||||||
|
emitter.off("DEL", listener);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
emitter.on("SET", listener);
|
const currentEmitter = (storage as any)?.[emitterSymbol];
|
||||||
emitter.on("DEL", listener);
|
if (currentEmitter) {
|
||||||
|
attach(storage);
|
||||||
|
} else if (typeof (storage as any)?.[syncAwaitSymbol] === "function") {
|
||||||
|
(storage as any)[syncAwaitSymbol](() => {
|
||||||
|
attach(storage);
|
||||||
|
forceUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
emitter.off("SET", listener);
|
unsubscribe?.();
|
||||||
emitter.off("DEL", listener);
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, [storage]);
|
||||||
|
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +156,7 @@ export function wrapSync<T extends Promise<any>>(store: T): Awaited<T> {
|
||||||
),
|
),
|
||||||
get(target, prop, recv) {
|
get(target, prop, recv) {
|
||||||
if (prop === syncAwaitSymbol) return awaitInit;
|
if (prop === syncAwaitSymbol) return awaitInit;
|
||||||
|
if (prop === emitterSymbol) return (awaited as any)?.[emitterSymbol];
|
||||||
return Reflect.get(awaited ?? target, prop, recv);
|
return Reflect.get(awaited ?? target, prop, recv);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
46
src/entry.ts
46
src/entry.ts
|
|
@ -10,21 +10,15 @@ async function initializeShiggyCord() {
|
||||||
// Make 'freeze' and 'seal' do nothing
|
// Make 'freeze' and 'seal' do nothing
|
||||||
Object.freeze = Object.seal = Object;
|
Object.freeze = Object.seal = Object;
|
||||||
|
|
||||||
await require("@metro/internals/caches").initMetroCache();
|
try {
|
||||||
|
await require("@metro/internals/caches").initMetroCache();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn("Failed to initialize metro cache, proceeding anyway:", e);
|
||||||
|
}
|
||||||
require(".").default();
|
require(".").default();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const { ClientInfoManager } = require("@lib/api/native/modules");
|
|
||||||
const stack = e instanceof Error ? e.stack : undefined;
|
const stack = e instanceof Error ? e.stack : undefined;
|
||||||
|
console.error("Failed to load ShiggyCord:", stack ?? e?.toString?.() ?? e);
|
||||||
console.log(stack ?? e?.toString?.() ?? e);
|
|
||||||
alert(
|
|
||||||
[
|
|
||||||
"Failed to load ShiggyCord!\n",
|
|
||||||
`Build Number: ${ClientInfoManager.getConstants().Build}`,
|
|
||||||
`ShiggyCord: ${version}`,
|
|
||||||
stack || e?.toString?.(),
|
|
||||||
].join("\n"),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,13 +103,29 @@ if (typeof window.__r === "undefined") {
|
||||||
}
|
}
|
||||||
|
|
||||||
const startDiscord = async () => {
|
const startDiscord = async () => {
|
||||||
await initializeShiggyCord();
|
try {
|
||||||
|
await initializeShiggyCord();
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Critical error during initializeShiggyCord:", err);
|
||||||
|
} finally {
|
||||||
|
for (const unpatch of unpatches) {
|
||||||
|
try {
|
||||||
|
unpatch();
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
unpatches.length = 0;
|
||||||
|
|
||||||
for (const unpatch of unpatches) unpatch();
|
try {
|
||||||
unpatches.length = 0;
|
originalRequire(0);
|
||||||
|
} catch (e) {
|
||||||
originalRequire(0);
|
console.error("originalRequire(0) error:", e);
|
||||||
resumeDeferred();
|
}
|
||||||
|
try {
|
||||||
|
resumeDeferred();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("resumeDeferred error:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
startDiscord();
|
startDiscord();
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,20 @@ import { ColorManifest, InternalColorDefinition } from "./types";
|
||||||
const tokenRef = findByProps("SemanticColor");
|
const tokenRef = findByProps("SemanticColor");
|
||||||
|
|
||||||
export function parseColorManifest(manifest: ColorManifest): InternalColorDefinition {
|
export function parseColorManifest(manifest: ColorManifest): InternalColorDefinition {
|
||||||
const resolveType = (type = "dark") => (colorsPref.type ?? type) === "dark" ? "darker" : "light";
|
const resolveType = (type = "dark") => {
|
||||||
|
const pref = colorsPref.type ?? type;
|
||||||
|
if (pref === "light") return "light";
|
||||||
|
if (tokenRef?.SemanticColor) {
|
||||||
|
const sample = Object.values(tokenRef.SemanticColor)[0] as Record<string, any> | undefined;
|
||||||
|
if (sample) {
|
||||||
|
if ("darker" in sample) return "darker";
|
||||||
|
if ("midnight" in sample) return "midnight";
|
||||||
|
if ("dark" in sample) return "dark";
|
||||||
|
if ("onyx" in sample) return "onyx";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "dark";
|
||||||
|
};
|
||||||
|
|
||||||
if (manifest.spec === 3) {
|
if (manifest.spec === 3) {
|
||||||
const semanticColorDefinitions: InternalColorDefinition["semantic"] = {};
|
const semanticColorDefinitions: InternalColorDefinition["semantic"] = {};
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,11 @@ import { _colorRef } from "@lib/addons/themes/colors/updater";
|
||||||
import { NativeThemeModule } from "@lib/api/native/modules";
|
import { NativeThemeModule } from "@lib/api/native/modules";
|
||||||
import { before, instead } from "@lib/api/patcher";
|
import { before, instead } from "@lib/api/patcher";
|
||||||
import { findByProps } from "@metro";
|
import { findByProps } from "@metro";
|
||||||
|
import { byMutableProp } from "@metro/filters";
|
||||||
|
import { createLazyModule } from "@metro/lazy";
|
||||||
import chroma from "chroma-js";
|
import chroma from "chroma-js";
|
||||||
|
|
||||||
const tokenReference = findByProps("SemanticColor");
|
const isThemeModule = createLazyModule(byMutableProp("isThemeDark"));
|
||||||
const themeTypes = findByProps("ThemeTypes")?.ThemeTypes;
|
|
||||||
|
|
||||||
const origRawColor = { ...tokenReference.RawColor };
|
|
||||||
const origDarker = themeTypes.DARKER as string;
|
|
||||||
const origLight = themeTypes.LIGHT as string;
|
|
||||||
|
|
||||||
const SEMANTIC_FALLBACK_MAP: Record<string, string> = {
|
const SEMANTIC_FALLBACK_MAP: Record<string, string> = {
|
||||||
"BG_BACKDROP": "BACKGROUND_FLOATING",
|
"BG_BACKDROP": "BACKGROUND_FLOATING",
|
||||||
|
|
@ -24,41 +21,57 @@ const SEMANTIC_FALLBACK_MAP: Record<string, string> = {
|
||||||
"BG_SURFACE_RAISED": "BACKGROUND_MOBILE_PRIMARY"
|
"BG_SURFACE_RAISED": "BACKGROUND_MOBILE_PRIMARY"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const alphaCache = new Map<string, string>();
|
||||||
|
let alphaCacheKey: string | null = null;
|
||||||
|
|
||||||
|
function cachedAlphaHex(hex: string, opacity: number): string {
|
||||||
|
if (alphaCacheKey !== _colorRef.key) {
|
||||||
|
alphaCache.clear();
|
||||||
|
alphaCacheKey = _colorRef.key;
|
||||||
|
}
|
||||||
|
const cacheKey = `${hex}|${opacity}`;
|
||||||
|
let result = alphaCache.get(cacheKey);
|
||||||
|
if (result === undefined) {
|
||||||
|
result = chroma(hex).alpha(opacity).hex();
|
||||||
|
alphaCache.set(cacheKey, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export default function patchDefinitionAndResolver() {
|
export default function patchDefinitionAndResolver() {
|
||||||
const callback = ([theme]: any[]) => theme === _colorRef.key ? [_colorRef.current!.reference] : void 0;
|
const tokenReference = findByProps("SemanticColor");
|
||||||
|
if (!tokenReference) return () => {};
|
||||||
|
|
||||||
Object.defineProperty(themeTypes, "DARKER", {
|
const origRawColor = { ...tokenReference.RawColor };
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => _colorRef.current?.reference === "darker" ? _colorRef.key : origDarker,
|
|
||||||
});
|
|
||||||
Object.defineProperty(themeTypes, "LIGHT", {
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => _colorRef.current?.reference === "light" ? _colorRef.key : origLight,
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(tokenReference.RawColor).forEach(key => {
|
const callback = ([theme]: any[]) => theme === _colorRef.key ? [_colorRef.current?.reference ?? "dark"] : void 0;
|
||||||
Object.defineProperty(tokenReference.RawColor, key, {
|
|
||||||
configurable: true,
|
if (tokenReference.RawColor) {
|
||||||
enumerable: true,
|
Object.keys(tokenReference.RawColor).forEach(key => {
|
||||||
get: () => {
|
Object.defineProperty(tokenReference.RawColor, key, {
|
||||||
const ret = _colorRef.current?.raw[key];
|
configurable: true,
|
||||||
if (ret) return ret;
|
enumerable: true,
|
||||||
return origRawColor[key];
|
get: () => {
|
||||||
}
|
const ret = _colorRef.current?.raw[key];
|
||||||
|
return ret || origRawColor[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const resolverTarget = tokenReference.default?.meta ?? tokenReference.default?.internal ?? tokenReference.default;
|
||||||
|
|
||||||
const unpatches = [
|
const unpatches = [
|
||||||
|
before("isThemeDark", isThemeModule, callback),
|
||||||
|
before("isThemeLight", isThemeModule, callback),
|
||||||
before("updateTheme", NativeThemeModule, callback),
|
before("updateTheme", NativeThemeModule, callback),
|
||||||
instead("resolveSemanticColor", tokenReference.default.meta ?? tokenReference.default.internal, (args: any[], orig: any) => {
|
resolverTarget ? instead("resolveSemanticColor", resolverTarget, (args: any[], orig: any) => {
|
||||||
if (!_colorRef.current) return orig(...args);
|
if (!_colorRef.current) return orig(...args);
|
||||||
if (args[0] !== _colorRef.key) return orig(...args);
|
if (args[0] !== _colorRef.key) return orig(...args);
|
||||||
|
|
||||||
args[0] = _colorRef.current.reference;
|
args[0] = _colorRef.current.reference;
|
||||||
|
|
||||||
const [name, colorDef] = extractInfo(_colorRef.current!.reference, args[1]);
|
const [name, colorDef] = extractInfo(_colorRef.current.reference, args[1], tokenReference);
|
||||||
|
|
||||||
let semanticDef = _colorRef.current.semantic[name];
|
let semanticDef = _colorRef.current.semantic[name];
|
||||||
if (!semanticDef && _colorRef.current.spec === 2 && name in SEMANTIC_FALLBACK_MAP) {
|
if (!semanticDef && _colorRef.current.spec === 2 && name in SEMANTIC_FALLBACK_MAP) {
|
||||||
|
|
@ -66,42 +79,40 @@ export default function patchDefinitionAndResolver() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (semanticDef?.value) {
|
if (semanticDef?.value) {
|
||||||
return semanticDef.opacity === 1
|
if (semanticDef.opacity === 1) return semanticDef.value;
|
||||||
? semanticDef.value
|
return cachedAlphaHex(semanticDef.value, semanticDef.opacity);
|
||||||
: chroma(semanticDef.value).alpha(semanticDef.opacity).hex();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const rawValue = _colorRef.current.raw[colorDef.raw];
|
if (colorDef?.raw) {
|
||||||
if (rawValue) {
|
const rawValue = _colorRef.current.raw[colorDef.raw];
|
||||||
// Set opacity if needed
|
if (rawValue) {
|
||||||
return colorDef.opacity === 1 ? rawValue : chroma(rawValue).alpha(colorDef.opacity).hex();
|
return colorDef.opacity === 1 ? rawValue : cachedAlphaHex(rawValue, colorDef.opacity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to default
|
|
||||||
return orig(...args);
|
return orig(...args);
|
||||||
}),
|
}) : () => {},
|
||||||
() => {
|
() => {
|
||||||
Object.defineProperty(themeTypes, "DARKER", {
|
if (tokenReference.RawColor) {
|
||||||
configurable: true, writable: true, value: origDarker
|
Object.defineProperty(tokenReference, "RawColor", {
|
||||||
});
|
configurable: true,
|
||||||
Object.defineProperty(themeTypes, "LIGHT", {
|
writable: true,
|
||||||
configurable: true, writable: true, value: origLight
|
value: origRawColor
|
||||||
});
|
});
|
||||||
Object.defineProperty(tokenReference, "RawColor", {
|
}
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: origRawColor
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
return () => unpatches.forEach(p => p());
|
return () => unpatches.forEach(p => p());
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractInfo(themeName: string, colorObj: any): [name: string, colorDef: any] {
|
function extractInfo(themeName: string, colorObj: any, tokenReference: any): [name: string, colorDef: any] {
|
||||||
|
if (!colorObj || typeof colorObj !== "object") return ["", null];
|
||||||
// @ts-ignore - assigning to extractInfo._sym
|
// @ts-ignore - assigning to extractInfo._sym
|
||||||
const propName = colorObj[extractInfo._sym ??= Object.getOwnPropertySymbols(colorObj)[0]];
|
const propName = colorObj[extractInfo._sym ??= Object.getOwnPropertySymbols(colorObj)[0]];
|
||||||
const colorDef = tokenReference.SemanticColor[propName];
|
const colorDef = tokenReference?.SemanticColor?.[propName];
|
||||||
|
if (!colorDef) return [propName, null];
|
||||||
|
|
||||||
return [propName, colorDef[themeName]];
|
const targetDef = colorDef[themeName] ?? colorDef.darker ?? colorDef.dark ?? colorDef.midnight ?? colorDef.onyx ?? Object.values(colorDef)[0];
|
||||||
|
return [propName, targetDef];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,6 @@ import { findByProps, findByPropsLazy, findByStoreNameLazy } from "@metro";
|
||||||
import { parseColorManifest } from "./parser";
|
import { parseColorManifest } from "./parser";
|
||||||
import { ColorManifest, InternalColorDefinition } from "./types";
|
import { ColorManifest, InternalColorDefinition } from "./types";
|
||||||
|
|
||||||
const tokenRef = findByProps("SemanticColor");
|
|
||||||
const origRawColor = { ...tokenRef.RawColor };
|
|
||||||
const AppearanceManager = findByPropsLazy("updateTheme");
|
const AppearanceManager = findByPropsLazy("updateTheme");
|
||||||
const ThemeStore = findByStoreNameLazy("ThemeStore");
|
const ThemeStore = findByStoreNameLazy("ThemeStore");
|
||||||
const FormDivider = findByPropsLazy("DIVIDER_COLORS");
|
const FormDivider = findByPropsLazy("DIVIDER_COLORS");
|
||||||
|
|
@ -23,63 +21,85 @@ interface InternalColorRef {
|
||||||
export const _colorRef: InternalColorRef = {
|
export const _colorRef: InternalColorRef = {
|
||||||
current: null,
|
current: null,
|
||||||
key: `bn-theme-${_inc}`,
|
key: `bn-theme-${_inc}`,
|
||||||
origRaw: origRawColor,
|
origRaw: {},
|
||||||
lastSetDiscordTheme: "darker"
|
lastSetDiscordTheme: "dark"
|
||||||
};
|
};
|
||||||
|
|
||||||
export function updateBunnyColor(colorManifest: ColorManifest | null, { update = true }) {
|
export function updateBunnyColor(colorManifest: ColorManifest | null, { update = true }) {
|
||||||
if (settings.safeMode?.enabled) return;
|
if (settings.safeMode?.enabled) return;
|
||||||
|
|
||||||
|
const tokenRef = findByProps("SemanticColor");
|
||||||
|
if (tokenRef?.RawColor && Object.keys(_colorRef.origRaw).length === 0) {
|
||||||
|
Object.assign(_colorRef.origRaw, tokenRef.RawColor);
|
||||||
|
}
|
||||||
|
|
||||||
const internalDef = colorManifest ? parseColorManifest(colorManifest) : null;
|
const internalDef = colorManifest ? parseColorManifest(colorManifest) : null;
|
||||||
|
const currentThemeName = ThemeStore?.theme ?? "dark";
|
||||||
const ref = Object.assign(_colorRef, {
|
const ref = Object.assign(_colorRef, {
|
||||||
current: internalDef,
|
current: internalDef,
|
||||||
key: `bn-theme-${++_inc}`,
|
key: `bn-theme-${++_inc}` as const,
|
||||||
lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-")
|
lastSetDiscordTheme: !currentThemeName.startsWith("bn-theme-")
|
||||||
? ThemeStore.theme
|
? currentThemeName
|
||||||
: _colorRef.lastSetDiscordTheme
|
: _colorRef.lastSetDiscordTheme
|
||||||
});
|
});
|
||||||
|
|
||||||
if (internalDef != null) {
|
if (internalDef != null && tokenRef) {
|
||||||
// Register the synthetic key BEFORE calling native, and verify it actually
|
|
||||||
// landed in every table native reads from. If any registration step fails,
|
|
||||||
// bail out to a known-good theme instead of handing native an unknown key.
|
|
||||||
try {
|
try {
|
||||||
tokenRef.Theme[ref.key.toUpperCase()] = ref.key;
|
if (tokenRef.Theme) {
|
||||||
|
tokenRef.Theme[ref.key.toUpperCase()] = ref.key;
|
||||||
if (!FormDivider.DIVIDER_COLORS[ref.current!.reference]) {
|
|
||||||
throw new Error(`Missing divider color reference: ${ref.current!.reference}`);
|
|
||||||
}
|
}
|
||||||
FormDivider.DIVIDER_COLORS[ref.key] = FormDivider.DIVIDER_COLORS[ref.current!.reference];
|
|
||||||
|
|
||||||
Object.keys(tokenRef.Shadow).forEach(k => {
|
const targetRef = ref.current?.reference ?? "dark";
|
||||||
if (!(ref.current!.reference in tokenRef.Shadow[k])) {
|
|
||||||
throw new Error(`Missing shadow reference: ${ref.current!.reference}`);
|
|
||||||
}
|
|
||||||
tokenRef.Shadow[k][ref.key] = tokenRef.Shadow[k][ref.current!.reference];
|
|
||||||
});
|
|
||||||
|
|
||||||
Object.keys(tokenRef.SemanticColor).forEach(k => {
|
if (FormDivider?.DIVIDER_COLORS) {
|
||||||
if (!(ref.current!.reference in tokenRef.SemanticColor[k])) {
|
const dividerFallback = FormDivider.DIVIDER_COLORS[targetRef] ??
|
||||||
throw new Error(`Missing semantic color reference: ${ref.current!.reference}`);
|
FormDivider.DIVIDER_COLORS.dark ??
|
||||||
}
|
FormDivider.DIVIDER_COLORS.darker ??
|
||||||
tokenRef.SemanticColor[k][ref.key] = {
|
Object.values(FormDivider.DIVIDER_COLORS)[0];
|
||||||
...tokenRef.SemanticColor[k][ref.current!.reference]
|
FormDivider.DIVIDER_COLORS[ref.key] = dividerFallback;
|
||||||
};
|
}
|
||||||
});
|
|
||||||
|
if (tokenRef.Shadow) {
|
||||||
|
Object.keys(tokenRef.Shadow).forEach(k => {
|
||||||
|
const shadowGroup = tokenRef.Shadow[k];
|
||||||
|
if (shadowGroup) {
|
||||||
|
const shadowFallback = shadowGroup[targetRef] ??
|
||||||
|
shadowGroup.dark ??
|
||||||
|
shadowGroup.darker ??
|
||||||
|
shadowGroup.midnight ??
|
||||||
|
shadowGroup.onyx ??
|
||||||
|
Object.values(shadowGroup)[0];
|
||||||
|
shadowGroup[ref.key] = shadowFallback;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenRef.SemanticColor) {
|
||||||
|
Object.keys(tokenRef.SemanticColor).forEach(k => {
|
||||||
|
const colorGroup = tokenRef.SemanticColor[k];
|
||||||
|
if (colorGroup) {
|
||||||
|
const colorFallback = colorGroup[targetRef] ??
|
||||||
|
colorGroup.dark ??
|
||||||
|
colorGroup.darker ??
|
||||||
|
colorGroup.midnight ??
|
||||||
|
colorGroup.onyx ??
|
||||||
|
Object.values(colorGroup)[0];
|
||||||
|
colorGroup[ref.key] = { ...colorFallback };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to register custom theme key, falling back", e);
|
console.error("Failed to register custom theme keys:", e);
|
||||||
ref.current = null; // force fallback path below
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update) {
|
if (update && AppearanceManager?.updateTheme) {
|
||||||
AppearanceManager.setShouldSyncAppearanceSettings(false);
|
try {
|
||||||
|
AppearanceManager.setShouldSyncAppearanceSettings?.(false);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
const targetTheme = ref.current != null ? ref.key : ref.lastSetDiscordTheme;
|
const targetTheme = ref.current != null ? ref.key : ref.lastSetDiscordTheme;
|
||||||
|
|
||||||
// This is the actual crash site: a raw call into native code with no
|
|
||||||
// guard. Wrap it so a native-side rejection can't take the whole
|
|
||||||
// bridge/thread down — fall back to the last known-good Discord theme.
|
|
||||||
try {
|
try {
|
||||||
AppearanceManager.updateTheme(targetTheme);
|
AppearanceManager.updateTheme(targetTheme);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
||||||
|
|
@ -33,10 +33,10 @@ function buildInitCache() {
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export async function initMetroCache() {
|
export async function initMetroCache() {
|
||||||
if (!await fileExists(BUNNY_METRO_CACHE_PATH)) return void buildInitCache();
|
|
||||||
const rawCache = await readFile(BUNNY_METRO_CACHE_PATH);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!await fileExists(BUNNY_METRO_CACHE_PATH)) return void buildInitCache();
|
||||||
|
const rawCache = await readFile(BUNNY_METRO_CACHE_PATH);
|
||||||
|
|
||||||
_metroCache = JSON.parse(rawCache);
|
_metroCache = JSON.parse(rawCache);
|
||||||
if (_metroCache._v !== CACHE_VERSION) {
|
if (_metroCache._v !== CACHE_VERSION) {
|
||||||
_metroCache = null!;
|
_metroCache = null!;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue