Merge branch 'kmmiio99o:main' into main
This commit is contained in:
commit
1450dc20af
7 changed files with 147 additions and 70 deletions
|
|
@ -70,6 +70,7 @@
|
|||
"MANUFACTURER": "Manufacturer",
|
||||
"MESSAGE": "Message",
|
||||
"MISCELLANEOUS": "Miscellaneous",
|
||||
"MODAL_RELOAD_THEME_CHANGE": "Reload the app to apply the theme change?",
|
||||
"MODAL_RELOAD_REQUIRED": "Reload app?",
|
||||
"MODAL_RELOAD_REQUIRED_DESC": "A reload is required to see the changes. Do you want to reload now?",
|
||||
"MODAL_THEME_REFETCHED": "Theme refetched",
|
||||
|
|
|
|||
|
|
@ -3,16 +3,40 @@ import { PluginInstanceInternal } from "@lib/addons/plugins/types";
|
|||
interface CorePlugin {
|
||||
default: PluginInstanceInternal;
|
||||
preenabled: boolean;
|
||||
loader: () => PluginInstanceInternal;
|
||||
}
|
||||
|
||||
type CorePluginRecord = Record<string, CorePlugin>;
|
||||
|
||||
function makeLazyPlugin(loaderFn: () => any, preenabled = true): CorePlugin {
|
||||
const container: any = {
|
||||
loader: () => {
|
||||
// loaderFn should synchronously require the module
|
||||
const mod = loaderFn();
|
||||
// module may be an object with `.default` or the instance directly
|
||||
const inst = mod && (mod.default ?? mod);
|
||||
return inst;
|
||||
},
|
||||
preenabled,
|
||||
get default() {
|
||||
// load and cache the instance on first access
|
||||
const inst = container.loader();
|
||||
Object.defineProperty(container, "default", { value: inst, writable: false, enumerable: true });
|
||||
return inst;
|
||||
},
|
||||
};
|
||||
|
||||
return container as CorePlugin;
|
||||
}
|
||||
|
||||
// Called from @lib/plugins
|
||||
export const getCorePlugins = (): Record<string, CorePlugin> => ({
|
||||
"bunny.quickinstall": { ...require("./quickinstall"), preenabled: true },
|
||||
"bunny.badges": require("./badges"),
|
||||
"bunny.notrack": require("./notrack"),
|
||||
"bunny.messagefix": require("./messagefix"),
|
||||
"bunny.fixembed": require("./fixembed"),
|
||||
"bunny.enhancements": require("./shiggyenhancements")
|
||||
export const getCorePlugins = (): CorePluginRecord => ({
|
||||
"bunny.quickinstall": makeLazyPlugin(() => require("./quickinstall"), true),
|
||||
"bunny.badges": makeLazyPlugin(() => require("./badges")),
|
||||
"bunny.notrack": makeLazyPlugin(() => require("./notrack")),
|
||||
"bunny.messagefix": makeLazyPlugin(() => require("./messagefix")),
|
||||
"bunny.fixembed": makeLazyPlugin(() => require("./fixembed")),
|
||||
"bunny.enhancements": makeLazyPlugin(() => require("./shiggyenhancements")),
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import {
|
|||
Text,
|
||||
} from "@metro/common/components";
|
||||
import { View, Image } from "react-native";
|
||||
import { showConfirmationAlert } from "@core/vendetta/alerts";
|
||||
import { BundleUpdaterManager } from "@lib/api/native/modules";
|
||||
|
||||
const { useReducer } = React;
|
||||
|
||||
|
|
@ -361,6 +363,47 @@ export default function Themes() {
|
|||
const navigation = NavigationNative.useNavigation();
|
||||
|
||||
const [mode, setMode] = React.useState<"installed" | "browse">("installed");
|
||||
|
||||
// Track the theme that was active when the user entered the Themes page.
|
||||
// If it changes while the user stays on this page, we'll prompt to reload when they leave.
|
||||
const initialThemeIdRef = React.useRef<string | null>(getCurrentTheme()?.id ?? null);
|
||||
|
||||
React.useEffect(() => {
|
||||
const beforeRemoveHandler = (e: any) => {
|
||||
try {
|
||||
const currentId = getCurrentTheme()?.id ?? null;
|
||||
if (currentId !== initialThemeIdRef.current) {
|
||||
// Notify the user that a reload is required for full application of theme changes
|
||||
showConfirmationAlert({
|
||||
title: Strings.MODAL_RELOAD_THEME_CHANGE,
|
||||
content: Strings.MODAL_THEME_REFETCHED_DESC,
|
||||
confirmText: Strings.RELOAD,
|
||||
cancelText: Strings.CANCEL,
|
||||
confirmColor: "red",
|
||||
onConfirm: () => {
|
||||
try {
|
||||
BundleUpdaterManager.reload();
|
||||
} catch (err) {
|
||||
try {
|
||||
(global as any).NativeModules?.BundleUpdaterManager?.reload?.();
|
||||
} catch (_) {}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// swallow any unexpected errors — we don't want to block navigation
|
||||
}
|
||||
};
|
||||
|
||||
const unsub = navigation?.addListener ? navigation.addListener("beforeRemove", beforeRemoveHandler) : undefined;
|
||||
return () => {
|
||||
try {
|
||||
if (unsub && typeof unsub === "function") unsub();
|
||||
else if (unsub && typeof unsub.remove === "function") unsub.remove();
|
||||
} catch (_) {}
|
||||
};
|
||||
}, [navigation]);
|
||||
const [themesList, setThemesList] = React.useState<ThemeData[]>([]);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [error, setError] = React.useState<string | null>(null);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import patchDefinitionAndResolver from "./patches/resolver";
|
|||
import patchStorage from "./patches/storage";
|
||||
import { ColorManifest } from "./types";
|
||||
import { updateBunnyColor } from "./updater";
|
||||
import fixStatusBar from "./statusbar";
|
||||
|
||||
/** @internal */
|
||||
export default function initColors(manifest: ColorManifest | null) {
|
||||
|
|
@ -11,8 +12,9 @@ export default function initColors(manifest: ColorManifest | null) {
|
|||
const patches = [
|
||||
patchStorage(),
|
||||
patchDefinitionAndResolver(),
|
||||
patchChatBackground()
|
||||
patchChatBackground(),
|
||||
fixStatusBar()
|
||||
];
|
||||
|
||||
return () => patches.forEach(p => p());
|
||||
}
|
||||
return () => patches.forEach(p => p && (typeof p === "function" ? p() : null));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@ const SEMANTIC_FALLBACK_MAP: Record<string, string> = {
|
|||
const origRawColor = { ...tokenReference.RawColor };
|
||||
|
||||
export default function patchDefinitionAndResolver() {
|
||||
const callback = ([theme]: any[]) => theme === _colorRef.key ? [_colorRef.current!.reference] : void 0;
|
||||
const callback = ([theme]: any[]) => {
|
||||
if (!_colorRef.current) return void 0;
|
||||
return theme === _colorRef.key ? [_colorRef.current.reference] : void 0;
|
||||
};
|
||||
|
||||
Object.keys(tokenReference.RawColor).forEach(key => {
|
||||
Object.defineProperty(tokenReference.RawColor, key, {
|
||||
|
|
@ -34,7 +37,7 @@ export default function patchDefinitionAndResolver() {
|
|||
get: () => {
|
||||
const ret = _colorRef.current?.raw[key];
|
||||
if (ret) return ret;
|
||||
|
||||
|
||||
return origRawColor[key];
|
||||
}
|
||||
});
|
||||
|
|
@ -46,15 +49,20 @@ export default function patchDefinitionAndResolver() {
|
|||
before("updateTheme", NativeThemeModule, callback),
|
||||
instead("resolveSemanticColor", tokenReference.default.meta ?? tokenReference.default.internal, (args: any[], orig: any) => {
|
||||
if (!_colorRef.current) return orig(...args);
|
||||
const currentRef = _colorRef.current;
|
||||
if (args[0] !== _colorRef.key) return orig(...args);
|
||||
|
||||
args[0] = _colorRef.current.reference;
|
||||
// Use the captured reference value
|
||||
args[0] = currentRef.reference;
|
||||
|
||||
const [name, colorDef] = extractInfo(_colorRef.current!.reference, args[1]);
|
||||
const [name, colorDef] = extractInfo(currentRef.reference, args[1]);
|
||||
|
||||
let semanticDef = _colorRef.current.semantic[name];
|
||||
if (!semanticDef && _colorRef.current.spec === 2 && name in SEMANTIC_FALLBACK_MAP) {
|
||||
semanticDef = _colorRef.current.semantic[SEMANTIC_FALLBACK_MAP[name]];
|
||||
// If extractInfo couldn't resolve a color definition, fall back to the original resolver
|
||||
if (!name || !colorDef) return orig(...args);
|
||||
|
||||
let semanticDef = currentRef.semantic[name];
|
||||
if (!semanticDef && currentRef.spec === 2 && name in SEMANTIC_FALLBACK_MAP) {
|
||||
semanticDef = currentRef.semantic[SEMANTIC_FALLBACK_MAP[name]];
|
||||
}
|
||||
|
||||
if (semanticDef?.value) {
|
||||
|
|
@ -62,10 +70,11 @@ export default function patchDefinitionAndResolver() {
|
|||
return chroma(semanticDef.value).alpha(semanticDef.opacity).hex();
|
||||
}
|
||||
|
||||
const rawValue = _colorRef.current.raw[colorDef.raw];
|
||||
const rawKey = colorDef.raw;
|
||||
const rawValue = rawKey ? currentRef.raw[rawKey] : undefined;
|
||||
if (rawValue) {
|
||||
// Set opacity if needed
|
||||
return colorDef.opacity === 1 ? rawValue : chroma(rawValue).alpha(colorDef.opacity).hex();
|
||||
return (colorDef.opacity === 1) ? rawValue : chroma(rawValue).alpha(colorDef.opacity).hex();
|
||||
}
|
||||
|
||||
// Fallback to default
|
||||
|
|
@ -86,7 +95,13 @@ export default function patchDefinitionAndResolver() {
|
|||
function extractInfo(themeName: string, colorObj: any): [name: string, colorDef: any] {
|
||||
// @ts-ignore - assigning to extractInfo._sym
|
||||
const propName = colorObj[extractInfo._sym ??= Object.getOwnPropertySymbols(colorObj)[0]];
|
||||
const colorDef = tokenReference.SemanticColor[propName];
|
||||
const entry = tokenReference.SemanticColor[propName];
|
||||
|
||||
return [propName, colorDef[themeName]];
|
||||
}
|
||||
if (!entry || typeof entry !== "object") {
|
||||
return [propName, undefined];
|
||||
}
|
||||
|
||||
const colorDef = Object.prototype.hasOwnProperty.call(entry, themeName) ? entry[themeName] : undefined;
|
||||
|
||||
return [propName, colorDef];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { StatusBar, TextInput, Platform } from "react-native";
|
||||
import { StatusBar, Platform } from "react-native";
|
||||
import { findByStoreNameLazy } from "@metro/wrappers";
|
||||
import { _colorRef } from "./updater";
|
||||
|
||||
|
|
@ -7,15 +7,8 @@ const ThemeStore = findByStoreNameLazy("ThemeStore");
|
|||
function getBarStyle() {
|
||||
// Check for custom theme
|
||||
const custom = _colorRef.current;
|
||||
if (custom) {
|
||||
// Bunny theme (spec 3)
|
||||
if (custom.spec === 3 && custom.reference) {
|
||||
return custom.reference === "light" ? "dark-content" : "light-content";
|
||||
}
|
||||
// Vendetta theme (spec 2)
|
||||
if (custom.spec === 2 && custom.reference) {
|
||||
return custom.reference === "light" ? "dark-content" : "light-content";
|
||||
}
|
||||
if (custom?.reference) {
|
||||
return custom.reference === "light" ? "dark-content" : "light-content";
|
||||
}
|
||||
|
||||
// Fallback to Discord theme string
|
||||
|
|
@ -25,33 +18,35 @@ function getBarStyle() {
|
|||
return (lowerTheme.includes("dark") || lowerTheme.includes("midnight")) ? "light-content" : "dark-content";
|
||||
}
|
||||
|
||||
return "dark-content";
|
||||
return "light-content";
|
||||
}
|
||||
|
||||
export default function fixStatusBar() {
|
||||
function applyStatusBar() {
|
||||
const style = getBarStyle();
|
||||
StatusBar.setBarStyle(style, true);
|
||||
|
||||
if (Platform.OS == "android") {
|
||||
if (Platform.OS === "android") {
|
||||
StatusBar.setTranslucent?.(true);
|
||||
StatusBar.setBackgroundColor?.("#00000000", true);
|
||||
}
|
||||
}
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
const origSetBarStyle = StatusBar.setBarStyle;
|
||||
StatusBar.setBarStyle = function (_style: any, ...args: any[]) {
|
||||
return origSetBarStyle.call(this, getBarStyle(), ...args);
|
||||
};
|
||||
}
|
||||
|
||||
if (Platform.OS == "ios") {
|
||||
function applyStatusBar() {
|
||||
const style = getBarStyle();
|
||||
StatusBar.setBarStyle(style, true);
|
||||
}
|
||||
const unsubscribe = ThemeStore?.addChangeListener?.(applyStatusBar);
|
||||
|
||||
const unsubscribe = ThemeStore?.addChangeListener?.(applyStatusBar);
|
||||
applyStatusBar();
|
||||
|
||||
StatusBar.setBarStyle(getBarStyle());
|
||||
|
||||
let delay = 200;
|
||||
for (let i = 0; i < 5; i++, delay *= 2) {
|
||||
setTimeout(applyStatusBar, delay);
|
||||
}
|
||||
|
||||
return unsubscribe;
|
||||
let delay = 200;
|
||||
for (let i = 0; i < 5; i++, delay *= 2) {
|
||||
setTimeout(applyStatusBar, delay);
|
||||
}
|
||||
|
||||
return unsubscribe;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ const origRawColor = { ...tokenRef.RawColor };
|
|||
const AppearanceManager = findByPropsLazy("updateTheme");
|
||||
const ThemeStore = findByStoreNameLazy("ThemeStore");
|
||||
const FormDivider = findByPropsLazy("DIVIDER_COLORS");
|
||||
import { colorsPref } from "./preferences";
|
||||
|
||||
let _inc = 1;
|
||||
|
||||
|
|
@ -25,32 +24,28 @@ export const _colorRef: InternalColorRef = {
|
|||
current: null,
|
||||
key: `bn-theme-${_inc}`,
|
||||
origRaw: origRawColor,
|
||||
lastSetDiscordTheme: "darker"
|
||||
lastSetDiscordTheme: "dark"
|
||||
};
|
||||
|
||||
export function updateBunnyColor(colorManifest: ColorManifest | null, { update = true } ) {
|
||||
if (settings.safeMode?.enabled) return;
|
||||
|
||||
const resolveType = (type = "dark") => (colorsPref.type ?? type) === "dark" ? "darker" : "light";
|
||||
const internalDef = colorManifest ? parseColorManifest(colorManifest) : null;
|
||||
|
||||
if (resolveType() == "light" || update) {
|
||||
var ref = Object.assign(_colorRef, {
|
||||
current: internalDef,
|
||||
key: `bn-theme-${++_inc}`,
|
||||
lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-") || !ThemeStore.theme.startsWith("darker")
|
||||
? ThemeStore.theme
|
||||
: _colorRef.lastSetDiscordTheme
|
||||
});
|
||||
} else {
|
||||
var ref = Object.assign(_colorRef, {
|
||||
current: internalDef,
|
||||
key: `dark`,
|
||||
lastSetDiscordTheme: !ThemeStore.theme.startsWith("bn-theme-") || !ThemeStore.theme.startsWith("darker")
|
||||
? ThemeStore.theme
|
||||
: _colorRef.lastSetDiscordTheme
|
||||
});
|
||||
}
|
||||
const keyName = internalDef && internalDef.reference === "light" ? `bn-theme-${++_inc}` : `dark`;
|
||||
|
||||
const tsTheme = typeof ThemeStore.theme === "string" ? ThemeStore.theme : undefined;
|
||||
const currentDiscordTheme = tsTheme === "darker" ? "dark" : (tsTheme ?? "");
|
||||
|
||||
const lastSet = (currentDiscordTheme && !currentDiscordTheme.startsWith("bn-theme-") && currentDiscordTheme !== keyName)
|
||||
? currentDiscordTheme
|
||||
: _colorRef.lastSetDiscordTheme;
|
||||
|
||||
var ref = Object.assign(_colorRef, {
|
||||
current: internalDef,
|
||||
key: keyName,
|
||||
lastSetDiscordTheme: lastSet
|
||||
});
|
||||
|
||||
if (internalDef != null) {
|
||||
tokenRef.Theme[ref.key.toUpperCase()] = ref.key;
|
||||
|
|
@ -66,7 +61,9 @@ export function updateBunnyColor(colorManifest: ColorManifest | null, { update =
|
|||
|
||||
if (update) {
|
||||
AppearanceManager.setShouldSyncAppearanceSettings(false);
|
||||
AppearanceManager.updateTheme(internalDef != null ? ref.key : "darker");
|
||||
const fallback = (_colorRef.lastSetDiscordTheme && !_colorRef.lastSetDiscordTheme.startsWith("bn-theme-"))
|
||||
? _colorRef.lastSetDiscordTheme
|
||||
: "dark";
|
||||
AppearanceManager.updateTheme(internalDef != null ? ref.key : fallback);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue