[UI] Fix You tab on recent versions (#142)

* Fix You tab crash

* Fix You tab patch on recent versions

Compatibility with older version is implemented, however, untested
This commit is contained in:
Amsyar Rasyiq 2023-08-28 08:36:19 +08:00 committed by GitHub
parent 3b035d537a
commit b90cddd3d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 37 deletions

View file

@ -1,19 +1,39 @@
import { initThemes } from "@lib/themes"; import { initThemes } from "@lib/themes";
import { instead } from "spitroast";
// Hoist required modules // Hoist required modules
// This used to be in filters.ts, but things became convoluted // This used to be in filters.ts, but things became convoluted
// Early find logic const basicFind = (filter: (m: any) => any | string) => {
const basicFind = (prop: string) => Object.values(window.modules).find(m => m?.publicModule.exports?.[prop])?.publicModule?.exports; 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 // 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 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 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 // Themes
if (window.__vendetta_loader?.features.themes) { if (window.__vendetta_loader?.features.themes) {

View file

@ -111,6 +111,7 @@ export const getYouData = () => {
return { return {
getLayout: () => ({ getLayout: () => ({
title: "Vendetta", title: "Vendetta",
label: "Vendetta",
// We can't use our keyMap function here since `settings` is an array not an object // We can't use our keyMap function here since `settings` is an array not an object
settings: getRenderableScreens(true).map(s => s.key) settings: getRenderableScreens(true).map(s => s.key)
}), }),
@ -124,6 +125,7 @@ export const getYouData = () => {
return { return {
type: "route", type: "route",
title: () => s.title,
icon: s.icon ? getAssetIDByName(s.icon) : null, icon: s.icon ? getAssetIDByName(s.icon) : null,
screen: { screen: {
// TODO: This is bad, we should not re-convert the key casing // TODO: This is bad, we should not re-convert the key casing

View file

@ -1,8 +1,16 @@
import { i18n } from "@metro/common";
import { findByProps } from "@metro/filters"; 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 { getRenderableScreens, getScreens, getYouData } from "@ui/settings/data";
import { i18n } from "@lib/metro/common";
export default function patchYou() {
const patches = new Array<Function>;
newYouPatch(patches) || oldYouPatch(patches);
return () => patches.forEach(p => p?.());
}
function oldYouPatch(patches: Function[]) {
const layoutModule = findByProps("useOverviewSettings"); const layoutModule = findByProps("useOverviewSettings");
const titleConfigModule = findByProps("getSettingTitleConfig"); const titleConfigModule = findByProps("getSettingTitleConfig");
const miscModule = findByProps("SETTING_RELATIONSHIPS", "SETTING_RENDERER_CONFIGS"); const miscModule = findByProps("SETTING_RELATIONSHIPS", "SETTING_RENDERER_CONFIGS");
@ -17,23 +25,13 @@ const usingNewGettersModule = !oldGettersModule;
const getterFunctionName = usingNewGettersModule ? NEW_GETTER_FUNCTION : OLD_GETTER_FUNCTION; const getterFunctionName = usingNewGettersModule ? NEW_GETTER_FUNCTION : OLD_GETTER_FUNCTION;
const gettersModule = oldGettersModule ?? findByProps(NEW_GETTER_FUNCTION); const gettersModule = oldGettersModule ?? findByProps(NEW_GETTER_FUNCTION);
export default function patchYou() {
if (!gettersModule || !layoutModule) return; if (!gettersModule || !layoutModule) return;
const patches = new Array<Function>;
const screens = getScreens(true); const screens = getScreens(true);
const renderableScreens = getRenderableScreens(true); const renderableScreens = getRenderableScreens(true);
const data = getYouData(); const data = getYouData();
patches.push(after("useOverviewSettings", layoutModule, (_, ret) => { patches.push(after("useOverviewSettings", layoutModule, (_, ret) => manipulateSections(ret, data.getLayout())));
// 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("getSettingTitleConfig", titleConfigModule, (_, ret) => ({ patches.push(after("getSettingTitleConfig", titleConfigModule, (_, ret) => ({
...ret, ...ret,
@ -53,16 +51,56 @@ export default function patchYou() {
...ret.filter((i: any) => (usingNewGettersModule || !screens.map(s => s.key).includes(i.setting))) ...ret.filter((i: any) => (usingNewGettersModule || !screens.map(s => s.key).includes(i.setting)))
].map((item, index, parent) => ({ ...item, index, total: parent.length })))); ].map((item, index, parent) => ({ ...item, index, total: parent.length }))));
// TODO: We could use a proxy for these
const oldRelationships = miscModule.SETTING_RELATIONSHIPS; const oldRelationships = miscModule.SETTING_RELATIONSHIPS;
miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };
const oldRendererConfigs = miscModule.SETTING_RENDERER_CONFIGS; const oldRendererConfigs = miscModule.SETTING_RENDERER_CONFIGS;
miscModule.SETTING_RELATIONSHIPS = { ...oldRelationships, ...data.relationships };
miscModule.SETTING_RENDERER_CONFIGS = { ...oldRendererConfigs, ...data.rendererConfigs }; miscModule.SETTING_RENDERER_CONFIGS = { ...oldRendererConfigs, ...data.rendererConfigs };
return () => { patches.push(() => {
miscModule.SETTING_RELATIONSHIPS = oldRelationships; miscModule.SETTING_RELATIONSHIPS = oldRelationships;
miscModule.SETTING_RENDERER_CONFIGS = oldRendererConfigs; 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")
} }