FakeProfileThemesAndEffects: fix the actual Effects crash (confirmed via adb logcat)

This commit is contained in:
Ally Founder 2026-08-10 21:31:31 -04:00
parent 67a9e63799
commit 411b6acc48
3 changed files with 46 additions and 28 deletions

View file

@ -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<FluxAction, "LOGOUT" | "PROFILE_EFFECTS_SET_TRY_IT_OUT" | "USER_PROFILE_EFFECTS_FETCH" | "USER_PROFILE_EFFECTS_FETCH_FAILURE" | "USER_PROFILE_EFFECTS_FETCH_SUCCESS">;

View file

@ -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<string, any>[];
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;

View file

@ -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: (
<EffectPickerActionSheet
effects={ProfileEffectStore.profileEffects}
onSelect={effect => {
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: (
<EffectPickerActionSheet
effects={profileEffectStore.profileEffects}
onSelect={effect => {
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.
}
}