Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
[UI] Fix You tab on recent versions (#142)
Browse files Browse the repository at this point in the history
* Fix You tab crash

* Fix You tab patch on recent versions

Compatibility with older version is implemented, however, untested
  • Loading branch information
pylixonly authored Aug 28, 2023
1 parent 3b035d5 commit b90cddd
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 36 deletions.
30 changes: 25 additions & 5 deletions src/lib/preinit.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import { initThemes } from "@lib/themes";
import { instead } from "spitroast";

// Hoist required modules
// This used to be in filters.ts, but things became convoluted

// Early find logic
const basicFind = (prop: string) => Object.values(window.modules).find(m => m?.publicModule.exports?.[prop])?.publicModule?.exports;
const basicFind = (filter: (m: any) => any | string) => {
for (const key in window.modules) {
const exp = window.modules[key]?.publicModule.exports;
if (exp && filter(exp)) return exp;
}
}

const requireNativeComponent = basicFind(m => m?.default?.name === "requireNativeComponent");

if (requireNativeComponent) {
// > "Tried to register two views with the same name DCDVisualEffectView"
// This serves as a workaround for the crashing You tab on Android starting from version 192.x
// How? We simply ignore it.
instead("default", requireNativeComponent, (args, orig) => {
try {
return orig(...args);
} catch {
return () => null;
}
})
}

// Hoist React on window
window.React = basicFind("createElement") as typeof import("react");
window.React = basicFind(m => m.createElement) as typeof import("react");

// Export ReactNative
export const ReactNative = basicFind("AppRegistry") as typeof import("react-native");
export const ReactNative = basicFind(m => m.AppRegistry) as typeof import("react-native");

// Export chroma.js
export const chroma = basicFind("brewer") as typeof import("chroma-js");
export const chroma = basicFind(m => m.brewer) as typeof import("chroma-js");

// Themes
if (window.__vendetta_loader?.features.themes) {
Expand Down
2 changes: 2 additions & 0 deletions src/ui/settings/data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export const getYouData = () => {
return {
getLayout: () => ({
title: "Vendetta",
label: "Vendetta",
// We can't use our keyMap function here since `settings` is an array not an object
settings: getRenderableScreens(true).map(s => s.key)
}),
Expand All @@ -124,6 +125,7 @@ export const getYouData = () => {

return {
type: "route",
title: () => s.title,
icon: s.icon ? getAssetIDByName(s.icon) : null,
screen: {
// TODO: This is bad, we should not re-convert the key casing
Expand Down
100 changes: 69 additions & 31 deletions src/ui/settings/patches/you.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,37 @@
import { i18n } from "@metro/common";
import { findByProps } from "@metro/filters";
import { after } from "@lib/patcher";
import { after, before } from "@lib/patcher";
import { getRenderableScreens, getScreens, getYouData } from "@ui/settings/data";
import { i18n } from "@lib/metro/common";

const layoutModule = findByProps("useOverviewSettings");
const titleConfigModule = findByProps("getSettingTitleConfig");
const miscModule = findByProps("SETTING_RELATIONSHIPS", "SETTING_RENDERER_CONFIGS");
export default function patchYou() {
const patches = new Array<Function>;

// Checks for 189.4 and above
// When dropping support for 189.3 and below, following can be done: (unless Discord changes things again)
// const gettersModule = findByProps("getSettingListItems");
const OLD_GETTER_FUNCTION = "getSettingSearchListItems";
const NEW_GETTER_FUNCTION = "getSettingListItems";
const oldGettersModule = findByProps(OLD_GETTER_FUNCTION);
const usingNewGettersModule = !oldGettersModule;
const getterFunctionName = usingNewGettersModule ? NEW_GETTER_FUNCTION : OLD_GETTER_FUNCTION;
const gettersModule = oldGettersModule ?? findByProps(NEW_GETTER_FUNCTION);
newYouPatch(patches) || oldYouPatch(patches);
return () => patches.forEach(p => p?.());
}

function oldYouPatch(patches: Function[]) {
const layoutModule = findByProps("useOverviewSettings");
const titleConfigModule = findByProps("getSettingTitleConfig");
const miscModule = findByProps("SETTING_RELATIONSHIPS", "SETTING_RENDERER_CONFIGS");

// Checks for 189.4 and above
// When dropping support for 189.3 and below, following can be done: (unless Discord changes things again)
// const gettersModule = findByProps("getSettingListItems");
const OLD_GETTER_FUNCTION = "getSettingSearchListItems";
const NEW_GETTER_FUNCTION = "getSettingListItems";
const oldGettersModule = findByProps(OLD_GETTER_FUNCTION);
const usingNewGettersModule = !oldGettersModule;
const getterFunctionName = usingNewGettersModule ? NEW_GETTER_FUNCTION : OLD_GETTER_FUNCTION;
const gettersModule = oldGettersModule ?? findByProps(NEW_GETTER_FUNCTION);

export default function patchYou() {
if (!gettersModule || !layoutModule) return;

const patches = new Array<Function>;
const screens = getScreens(true);
const renderableScreens = getRenderableScreens(true);
const data = getYouData();

patches.push(after("useOverviewSettings", layoutModule, (_, ret) => {
// Add our settings
const accountSettingsIndex = ret.findIndex((i: any) => i.title === i18n.Messages.ACCOUNT_SETTINGS);
ret.splice(accountSettingsIndex + 1, 0, data.getLayout());

// Upload Logs button be gone
const supportCategory = ret.find((i: any) => i.title === i18n.Messages.SUPPORT);
supportCategory.settings = supportCategory.settings.filter((s: string) => s !== "UPLOAD_DEBUG_LOGS");
}));
patches.push(after("useOverviewSettings", layoutModule, (_, ret) => manipulateSections(ret, data.getLayout())));

patches.push(after("getSettingTitleConfig", titleConfigModule, (_, ret) => ({
...ret,
Expand All @@ -53,16 +51,56 @@ export default function patchYou() {
...ret.filter((i: any) => (usingNewGettersModule || !screens.map(s => s.key).includes(i.setting)))
].map((item, index, parent) => ({ ...item, index, total: parent.length }))));

// TODO: We could use a proxy for these
const oldRelationships = miscModule.SETTING_RELATIONSHIPS;
miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };

const oldRendererConfigs = miscModule.SETTING_RENDERER_CONFIGS;

miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };
miscModule.SETTING_RENDERER_CONFIGS = { ...oldRendererConfigs, ...data.rendererConfigs };

return () => {
patches.push(() => {
miscModule.SETTING_RELATIONSHIPS = oldRelationships;
miscModule.SETTING_RENDERER_CONFIGS = oldRendererConfigs;
patches.forEach(p => p());
};
});

return true;
}

function newYouPatch(patches: Function[]) {
const settingsListComponents = findByProps("SearchableSettingsList");
const settingConstantsModule = findByProps("SETTING_RENDERER_CONFIG");
const gettersModule = findByProps("getSettingListItems");

if (!gettersModule || !settingsListComponents || !settingConstantsModule) return false;

const screens = getScreens(true);
const data = getYouData();

patches.push(before("type", settingsListComponents.SearchableSettingsList, ([{ sections }]) => manipulateSections(sections, data.getLayout())));

patches.push(after("getSettingListSearchResultItems", gettersModule, (_, ret) => {
ret.forEach((s: any) => screens.some(b => b.key === s.setting) && (s.breadcrumbs = ["Vendetta"]))
}));

const oldRendererConfig = settingConstantsModule.SETTING_RENDERER_CONFIG;
settingConstantsModule.SETTING_RENDERER_CONFIG = { ...oldRendererConfig, ...data.rendererConfigs };

patches.push(() => {
settingConstantsModule.SETTING_RENDERER_CONFIG = oldRendererConfig;
});

return true;
}

const isLabel = (i: any, name: string) => i?.label === name || i?.title === name;

function manipulateSections(sections: any[], layout: any) {
if (!Array.isArray(sections) || sections.find((i: any) => isLabel(i, "Vendetta"))) return;

// Add our settings
const accountSettingsIndex = sections.findIndex((i: any) => isLabel(i, i18n.Messages.ACCOUNT_SETTINGS));
sections.splice(accountSettingsIndex + 1, 0, layout);

// Upload Logs button be gone
const supportCategory = sections.find((i: any) => isLabel(i, i18n.Messages.SUPPORT));
if (supportCategory) supportCategory.settings = supportCategory.settings.filter((s: string) => s !== "UPLOAD_DEBUG_LOGS")
}

0 comments on commit b90cddd

Please sign in to comment.