From 411b6acc488f112d38e08633e7350116c4c351f8 Mon Sep 17 00:00:00 2001 From: Ally Founder Date: Mon, 10 Aug 2026 21:31:31 -0400 Subject: [PATCH] FakeProfileThemesAndEffects: fix the actual Effects crash (confirmed via adb logcat) --- .../src/lib/stores/ProfileEffectStore.ts | 8 ++- .../patches/patchUseProfileEffectSections.ts | 9 +-- .../src/ui/actionSheets/showEffectPicker.tsx | 57 ++++++++++++------- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/plugins/fake-profile-themes-and-effects/src/lib/stores/ProfileEffectStore.ts b/plugins/fake-profile-themes-and-effects/src/lib/stores/ProfileEffectStore.ts index a67ac8c..e29bb4b 100644 --- a/plugins/fake-profile-themes-and-effects/src/lib/stores/ProfileEffectStore.ts +++ b/plugins/fake-profile-themes-and-effects/src/lib/stores/ProfileEffectStore.ts @@ -1,10 +1,14 @@ import type { ExtractAction, FluxAction, FluxStore } from "@vencord/discord-types"; import { findByStoreName } from "@vendetta/metro"; +import { lazy } from "@shared/lib/lazy"; import type { CollectiblesItemType } from "@fpte/lib/records"; -export const ProfileEffectStore: $ProfileEffectStore - = findByStoreName("ProfileEffectStore"); +// findByStoreName caches a negative result forever - a real live crash confirmed this store +// wasn't registered yet at plugin-load time, and the eager version of this permanently poisoned +// itself with undefined for the rest of the session. Every call site now has to call this as a +// function and handle it possibly still being undefined, instead of importing a plain constant. +export const getProfileEffectStore = lazy((): $ProfileEffectStore | undefined => findByStoreName("ProfileEffectStore")); export type ProfileEffectStoreAction = ExtractAction; diff --git a/plugins/fake-profile-themes-and-effects/src/patches/patchUseProfileEffectSections.ts b/plugins/fake-profile-themes-and-effects/src/patches/patchUseProfileEffectSections.ts index 59ddf3f..d468f8c 100644 --- a/plugins/fake-profile-themes-and-effects/src/patches/patchUseProfileEffectSections.ts +++ b/plugins/fake-profile-themes-and-effects/src/patches/patchUseProfileEffectSections.ts @@ -2,7 +2,7 @@ import { findByPropsAll } from "@vendetta/metro"; import { after } from "@vendetta/patcher"; import { ProfileEffectRecord } from "@fpte/lib/records"; -import { type ProfileEffect, ProfileEffectStore } from "@fpte/lib/stores"; +import { type ProfileEffect, getProfileEffectStore } from "@fpte/lib/stores"; import { previewUserId } from "@fpte/patches/patchUseProfileTheme"; const useSectionModules = findByPropsAll("NONE_ITEM"); @@ -14,12 +14,13 @@ let prevSections: Record[]; export const patchUseProfileEffectSections = () => useSectionModules.map( sectionModule => after("default", sectionModule, (_args: unknown[], origSections: typeof prevSections) => { try { - if (previewUserId && origSections?.[0]?.items) { - const currProfileEffects = ProfileEffectStore.profileEffects; + const profileEffectStore = getProfileEffectStore(); + if (previewUserId && profileEffectStore && origSections?.[0]?.items) { + const currProfileEffects = profileEffectStore.profileEffects; if (prevProfileEffects !== currProfileEffects) { origSections.splice(1); origSections[0]!.items.splice(1); - ProfileEffectStore.profileEffects.forEach(effect => { + profileEffectStore.profileEffects.forEach(effect => { origSections[0]!.items.push(new ProfileEffectRecord(effect)); }); prevSections = origSections; diff --git a/plugins/fake-profile-themes-and-effects/src/ui/actionSheets/showEffectPicker.tsx b/plugins/fake-profile-themes-and-effects/src/ui/actionSheets/showEffectPicker.tsx index fe85f0d..c2109f3 100644 --- a/plugins/fake-profile-themes-and-effects/src/ui/actionSheets/showEffectPicker.tsx +++ b/plugins/fake-profile-themes-and-effects/src/ui/actionSheets/showEffectPicker.tsx @@ -1,7 +1,7 @@ import React from "react"; import { FluxDispatcher } from "@fpte/lib/flux"; -import { type ProfileEffectConfig, ProfileEffectStore, UserStore } from "@fpte/lib/stores"; +import { type ProfileEffectConfig, getProfileEffectStore, UserStore } from "@fpte/lib/stores"; import { setPreviewUserId } from "@fpte/patches/patchUseProfileTheme"; import { EffectPickerActionSheet, hideActionSheet, showActionSheet } from "@fpte/ui/actionSheets"; @@ -11,26 +11,39 @@ export function showEffectPicker( onSelect: (effect: ProfileEffectConfig | null) => void, currentEffectId?: string | undefined ) { - function onClose(action: any) { - if (action.key === SHEET_KEY) { - FluxDispatcher.unsubscribe("HIDE_ACTION_SHEET", onClose); - setPreviewUserId(undefined); - } - } - FluxDispatcher.subscribe("HIDE_ACTION_SHEET", onClose); + // Called directly from a native onPress with nothing else between it and the touch dispatch - + // an uncaught throw here is fatal to the whole app (confirmed live via adb logcat: a plain + // TypeError here surfaced as AndroidRuntime FATAL EXCEPTION, not a recoverable React error + // screen), so this needs its own try/catch rather than relying on React's own error handling. + try { + const user = UserStore.getCurrentUser(); + const profileEffectStore = getProfileEffectStore(); + if (!user || !profileEffectStore) return; - showActionSheet({ - content: ( - { - onSelect(effect); - hideActionSheet(SHEET_KEY); - }} - user={UserStore.getCurrentUser()!} - currentEffectId={currentEffectId} - /> - ), - key: SHEET_KEY - }); + function onClose(action: any) { + if (action.key === SHEET_KEY) { + FluxDispatcher.unsubscribe("HIDE_ACTION_SHEET", onClose); + setPreviewUserId(undefined); + } + } + FluxDispatcher.subscribe("HIDE_ACTION_SHEET", onClose); + + showActionSheet({ + content: ( + { + onSelect(effect); + hideActionSheet(SHEET_KEY); + }} + user={user} + currentEffectId={currentEffectId} + /> + ), + key: SHEET_KEY + }); + } catch { + // Nothing sensible to fall back to for a picker that couldn't even open - just don't + // crash the app over it. + } }