From fc5eb71659f964bfc87a09fcdaa782e2de78e7cc Mon Sep 17 00:00:00 2001 From: kmmiio99o Date: Sun, 26 Jul 2026 16:00:10 +0200 Subject: [PATCH] feat: nuke nexxlib --- nexxstuff/components/ActionSheet.tsx | 82 ------ nexxstuff/components/AutoRow.tsx | 50 ---- nexxstuff/components/BetterTableRow.tsx | 111 -------- nexxstuff/components/InlineCheckbox.tsx | 43 --- nexxstuff/components/Modal.tsx | 27 -- nexxstuff/components/RichText.tsx | 34 --- nexxstuff/components/Skeleton.tsx | 36 --- nexxstuff/components/SliderIcon.tsx | 50 ---- nexxstuff/components/SmartMention.tsx | 49 ---- nexxstuff/components/SuperAwesomeIcon.tsx | 53 ---- nexxstuff/components/Text.tsx | 78 ------ nexxstuff/components/TextBadge.tsx | 104 ------- nexxstuff/components/sheets/ChooseSheet.tsx | 58 ---- nexxstuff/deps.ts | 227 --------------- nexxstuff/hooks/usePromise.ts | 62 ----- nexxstuff/lib/intlProxy.ts | 68 ----- nexxstuff/lib/pinToSettings/index.ts | 30 -- nexxstuff/lib/pinToSettings/panel.tsx | 91 ------ nexxstuff/lib/pinToSettings/tabs.tsx | 105 ------- nexxstuff/lib/redesign/index.tsx | 75 ----- nexxstuff/lib/redesign/types.ts | 175 ------------ nexxstuff/lib/ui.ts | 17 -- nexxstuff/migration.tsx | 294 -------------------- nexxstuff/types.tsx | 262 ----------------- nexxstuff/typings.d.ts | 177 ------------ tsconfig.json | 4 - 26 files changed, 2362 deletions(-) delete mode 100644 nexxstuff/components/ActionSheet.tsx delete mode 100644 nexxstuff/components/AutoRow.tsx delete mode 100644 nexxstuff/components/BetterTableRow.tsx delete mode 100644 nexxstuff/components/InlineCheckbox.tsx delete mode 100644 nexxstuff/components/Modal.tsx delete mode 100644 nexxstuff/components/RichText.tsx delete mode 100644 nexxstuff/components/Skeleton.tsx delete mode 100644 nexxstuff/components/SliderIcon.tsx delete mode 100644 nexxstuff/components/SmartMention.tsx delete mode 100644 nexxstuff/components/SuperAwesomeIcon.tsx delete mode 100644 nexxstuff/components/Text.tsx delete mode 100644 nexxstuff/components/TextBadge.tsx delete mode 100644 nexxstuff/components/sheets/ChooseSheet.tsx delete mode 100644 nexxstuff/deps.ts delete mode 100644 nexxstuff/hooks/usePromise.ts delete mode 100644 nexxstuff/lib/intlProxy.ts delete mode 100644 nexxstuff/lib/pinToSettings/index.ts delete mode 100644 nexxstuff/lib/pinToSettings/panel.tsx delete mode 100644 nexxstuff/lib/pinToSettings/tabs.tsx delete mode 100644 nexxstuff/lib/redesign/index.tsx delete mode 100644 nexxstuff/lib/redesign/types.ts delete mode 100644 nexxstuff/lib/ui.ts delete mode 100644 nexxstuff/migration.tsx delete mode 100644 nexxstuff/types.tsx delete mode 100644 nexxstuff/typings.d.ts diff --git a/nexxstuff/components/ActionSheet.tsx b/nexxstuff/components/ActionSheet.tsx deleted file mode 100644 index 118d276..0000000 --- a/nexxstuff/components/ActionSheet.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { find, findByProps } from "@vendetta/metro"; -import { ReactNative as RN } from "@vendetta/metro/common"; -import { without } from "@vendetta/utils"; -import type { ImageSourcePropType, ViewProps } from "react-native"; - -const _ActionSheet = findByProps("ActionSheet")?.ActionSheet - ?? find(x => x.render?.name === "ActionSheet"); // thank you to @pylixonly for fixing this - -const { ActionSheetTitleHeader, ActionSheetCloseButton } = findByProps( - "ActionSheetTitleHeader", - "ActionSheetCloseButton", -); - -export const LazyActionSheet = findByProps("openLazy", "hideActionSheet") as { - openLazy: (component: Promise, key: string, props?: object) => void; - hideActionSheet: () => void; -}; -export const { openLazy, hideActionSheet } = LazyActionSheet; - -export const { showSimpleActionSheet } = findByProps( - "showSimpleActionSheet", -) as { - showSimpleActionSheet: (props: { - key: "CardOverflow"; - header: { - title: string; - subtitle?: string; - icon?: React.ReactNode; - onClose?: () => void; - }; - options: { - label: string; - icon?: ImageSourcePropType; - isDestructive?: boolean; - onPress?: () => void; - }[]; - }) => void; -}; - -type ActionSheetProps = React.PropsWithChildren< - ViewProps & { - title: string; - onClose?: () => void; - } ->; - -export const ActionSheet = ((props: ActionSheetProps) => { - return ( - <_ActionSheet> - { - hideActionSheet(); - })} - /> - } - /> - - - ); -}) as { - (props: ActionSheetProps): JSX.Element; - open: >( - sheet: Sheet, - props: Parameters[0], - ) => void; -}; - -ActionSheet.open = (sheet, props) => { - openLazy( - new Promise(res => { - res({ - default: sheet, - }); - }) as any, - "ActionSheet", - props, - ); -}; diff --git a/nexxstuff/components/AutoRow.tsx b/nexxstuff/components/AutoRow.tsx deleted file mode 100644 index 42e72ef..0000000 --- a/nexxstuff/components/AutoRow.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { findByProps } from "@vendetta/metro"; -import { ReactNative as RN, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; -import { Forms } from "@vendetta/ui/components"; - -const { FormRow } = Forms; -const { TableRow } = findByProps("TableRow"); - -export const { useInMainTabsExperiment } = findByProps( - "useInMainTabsExperiment", - "isInMainTabsExperiment", -); - -export default function AutoRow({ - label, - icon, - onPress, -}: { - label: string; - icon: number; - onPress?: () => void; -}) { - const styles = stylesheet.createThemedStyleSheet({ - icon: { - width: 24, - height: 24, - tintColor: semanticColors.INTERACTIVE_ICON_DEFAULT, - opacity: 0.6, - }, - }); - const tabbed = useInMainTabsExperiment(); - - if (tabbed) { - return ( - } - onPress={onPress} - /> - ); - } - - return ( - } - onPress={onPress} - /> - ); -} diff --git a/nexxstuff/components/BetterTableRow.tsx b/nexxstuff/components/BetterTableRow.tsx deleted file mode 100644 index 42f7f4a..0000000 --- a/nexxstuff/components/BetterTableRow.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { ReactNative as RN, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; - -import Text from "./Text"; - -export function BetterTableRowTitle({ - title, - onPress, - icon, - padding, -}: { - title: string; - onPress?: () => void; - icon?: number; - padding?: boolean; -}) { - const styles = stylesheet.createThemedStyleSheet({ - androidRipple: { - color: semanticColors.ANDROID_RIPPLE, - cornerRadius: 4, - } as any, - icon: { - width: 16, - height: 16, - marginTop: 1.5, - tintColor: semanticColors.TEXT_MUTED, - }, - }); - const UseCompontent = onPress ? RN.Pressable : RN.View; - - return ( - - {icon && ( - - )} - - {title} - - - ); -} - -export function BetterTableRowGroup({ - title, - onTitlePress, - icon, - children, - padding, - nearby, -}: React.PropsWithChildren<{ - title?: string | React.ReactNode; - onTitlePress?: () => void; - icon?: number; - padding?: boolean; - nearby?: boolean; -}>) { - const styles = stylesheet.createThemedStyleSheet({ - main: { - backgroundColor: semanticColors.CARD_BACKGROUND_DEFAULT, - borderColor: semanticColors.BORDER_MUTED, - borderWidth: 1, - borderRadius: 16, - overflow: "hidden", - flex: 1, - }, - }); - - return ( - - {title - ? ( - typeof title === "string" - ? ( - - ) - : title - ) - : null} - - {padding - ? ( - - {children} - - ) - : children} - - - ); -} diff --git a/nexxstuff/components/InlineCheckbox.tsx b/nexxstuff/components/InlineCheckbox.tsx deleted file mode 100644 index fea51c4..0000000 --- a/nexxstuff/components/InlineCheckbox.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { find } from "@vendetta/metro"; -import { React, ReactNative as RN } from "@vendetta/metro/common"; - -import Text from "./Text"; - -const { FormCheckbox } = find(x => x?.FormCheckbox && !x?.FormCardSection); - -export default function InlineCheckbox({ - label, - initialValue, - update, -}: { - label: string; - initialValue: boolean; - update: (val: boolean) => void; -}) { - const [value, setValue] = React.useState(initialValue); - update(value); - - return ( - { - setValue(!value); - }} - style={{ - flexDirection: "row", - alignItems: "center", - gap: 8, - marginTop: 12, - }} - > - - - {label} - - - ); -} diff --git a/nexxstuff/components/Modal.tsx b/nexxstuff/components/Modal.tsx deleted file mode 100644 index 06744f2..0000000 --- a/nexxstuff/components/Modal.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { without } from "@vendetta/utils"; - -import { modalCloseButton, Navigator, popModal } from "../types"; - -export default function Modal( - props: React.PropsWithChildren<{ - mkey: string; - headerRight?: React.FunctionComponent; - title?: string; - }>, -) { - if (!Navigator || !modalCloseButton) return null; - return ( - popModal(props.mkey)), - render: () => props.children, - }, - ), - }} - /> - ); -} diff --git a/nexxstuff/components/RichText.tsx b/nexxstuff/components/RichText.tsx deleted file mode 100644 index b8b5535..0000000 --- a/nexxstuff/components/RichText.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { ReactNative as RN } from "@vendetta/metro/common"; - -import Text from "./Text"; - -export namespace RichText { - export function Bold({ - children, - onPress, - }: React.PropsWithChildren<{ - onPress?: () => void; - }>) { - return ( - - {children} - - ); - } - - export function Underline({ - children, - onPress, - }: React.PropsWithChildren<{ - onPress?: () => void; - }>) { - return ( - - {children} - - ); - } -} diff --git a/nexxstuff/components/Skeleton.tsx b/nexxstuff/components/Skeleton.tsx deleted file mode 100644 index 5b22eba..0000000 --- a/nexxstuff/components/Skeleton.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { React, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; -import type { DimensionValue, ViewStyle } from "react-native"; - -import { Reanimated } from "$/deps"; - -export default function Skeleton({ - height, - style, -}: { - height: DimensionValue; - style?: ViewStyle; -}) { - const baseStyle = stylesheet.createThemedStyleSheet({ - skeleton: { - height, - borderRadius: 16, - backgroundColor: semanticColors.BACKGROUND_MOD_STRONG, - }, - }); - - const opacity = Reanimated.useSharedValue(1); - React.useEffect(() => { - opacity.value = Reanimated.withRepeat( - Reanimated.withTiming(0.5, { duration: 1000 }), - -1, - true, - ); - }, []); - - return ( - - ); -} diff --git a/nexxstuff/components/SliderIcon.tsx b/nexxstuff/components/SliderIcon.tsx deleted file mode 100644 index 8711215..0000000 --- a/nexxstuff/components/SliderIcon.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { ReactNative as RN, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; -import { getAssetIDByName } from "@vendetta/ui/assets"; - -export default function SliderIcon({ - side, - onPress, -}: { - side: "start" | "end"; - onPress: () => void; -}) { - const styles = stylesheet.createThemedStyleSheet({ - androidRipple: { - color: semanticColors.ANDROID_RIPPLE, - }, - iconSecondary: { - width: 24, - height: 24, - tintColor: "transparent", - }, - icon: { - width: 24, - height: 24, - tintColor: semanticColors.INTERACTIVE_ICON_DEFAULT, - position: "absolute", - top: 0, - }, - }); - - const icon = side === "start" ? "CircleMinusIcon" : "CirclePlusIcon"; - - return ( - - - - - - - ); -} diff --git a/nexxstuff/components/SmartMention.tsx b/nexxstuff/components/SmartMention.tsx deleted file mode 100644 index 7f8b320..0000000 --- a/nexxstuff/components/SmartMention.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { findByProps, findByStoreName } from "@vendetta/metro"; -import { React } from "@vendetta/metro/common"; - -import Text from "./Text"; - -const { showUserProfile } = findByProps("showUserProfile"); -const { fetchProfile } = findByProps("fetchProfile"); - -const UserStore = findByStoreName("UserStore"); - -export default function SmartMention({ - userId, - color, - loadUsername, - children, -}: React.PropsWithChildren<{ - userId: string; - color?: string; - loadUsername?: boolean; -}>) { - const [loadedUsername, setLoadedUsername] = React.useState( - null, - ); - - React.useEffect( - () => - !loadedUsername - && loadUsername - && (UserStore.getUser(userId) - ? setLoadedUsername(UserStore.getUser(userId).username) - : fetchProfile(userId).then(x => { - setLoadedUsername(x.user.username); - })), - [loadUsername], - ); - - return ( - - UserStore.getUser(userId) - ? showUserProfile({ userId }) - : fetchProfile(userId).then(() => showUserProfile({ userId }))} - > - {loadUsername ? `@${loadedUsername ?? "..."}` : children} - - ); -} diff --git a/nexxstuff/components/SuperAwesomeIcon.tsx b/nexxstuff/components/SuperAwesomeIcon.tsx deleted file mode 100644 index 39d2ac8..0000000 --- a/nexxstuff/components/SuperAwesomeIcon.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { ReactNative as RN, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; - -export default function SuperAwesomeIcon({ - onPress, - onLongPress, - icon, - style, - destructive, - color, -}: { - onPress?: () => void; - onLongPress?: () => void; - destructive?: boolean; - color?: any; - icon: number; - style: "header" | "card" | import("react-native").ImageStyle; -}) { - const styles = stylesheet.createThemedStyleSheet({ - headerStyleIcon: { - width: 24, - height: 24, - marginRight: 10, - tintColor: semanticColors.TEXT_STRONG, - }, - cardStyleIcon: { - width: 22, - height: 22, - marginLeft: 5, - tintColor: semanticColors.INTERACTIVE_ICON_DEFAULT, - }, - destructiveIcon: { - tintColor: semanticColors.TEXT_FEEDBACK_CRITICAL, - }, - }); - - return ( - - !!x)} - source={icon} - /> - - ); -} diff --git a/nexxstuff/components/Text.tsx b/nexxstuff/components/Text.tsx deleted file mode 100644 index 366a6df..0000000 --- a/nexxstuff/components/Text.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { findByProps } from "@vendetta/metro"; -import { React, ReactNative as RN } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; -import type { TextProps } from "react-native"; - -import { resolveSemanticColor, TextStyleSheet, type TextStyleSheetVariant } from "../types"; - -export function TrailingText({ children }: React.PropsWithChildren) { - return ( - - {children} - - ); -} - -const { useThemeContext } = findByProps("useThemeContext"); - -export default function Text({ - variant, - lineClamp, - color, - align, - style, - onPress, - getChildren, - children, - liveUpdate, - ellipsis, -}: React.PropsWithChildren<{ - variant?: TextStyleSheetVariant; - color?: string; - getChildren?: () => React.ReactNode | undefined; - liveUpdate?: boolean; - - lineClamp?: TextProps["numberOfLines"]; - align?: "left" | "right" | "center"; - style?: TextProps["style"]; - onPress?: TextProps["onPress"]; - ellipsis?: TextProps["ellipsizeMode"]; -}>) { - const themeContext = useThemeContext(); - const [_, forceUpdate] = React.useReducer(x => ~x, 0); - - React.useEffect(() => { - if (!liveUpdate) return; - const nextSecond = () => Date.now() - new Date().setMilliseconds(1000); - - let timeout = setTimeout(function update() { - forceUpdate(); - timeout = setTimeout(update, nextSecond()); - }, nextSecond()); - - return () => clearTimeout(timeout); - }, []); - - return ( - - {getChildren?.() ?? children} - - ); -} diff --git a/nexxstuff/components/TextBadge.tsx b/nexxstuff/components/TextBadge.tsx deleted file mode 100644 index 630f44b..0000000 --- a/nexxstuff/components/TextBadge.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { React, ReactNative as RN, stylesheet } from "@vendetta/metro/common"; -import { semanticColors } from "@vendetta/ui"; -import type { ViewStyle } from "react-native"; - -import Text from "$/components/Text"; -import { Reanimated } from "$/deps"; - -export default function TextBadge({ - style, - variant, - children, - shiny, -}: React.PropsWithChildren<{ - style?: ViewStyle; - variant: "primary" | "destructive"; - shiny?: boolean; -}>) { - const [width, setWidth] = React.useState(0); - - const styles = stylesheet.createThemedStyleSheet({ - main: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - borderRadius: 2147483647, - paddingHorizontal: 6, - paddingVertical: 3, - backgroundColor: semanticColors[ - `REDESIGN_BUTTON_${variant.toUpperCase()}_BACKGROUND` - ], - marginTop: 3, - overflow: "hidden", - }, - shiner: { - position: "absolute", - width: 7, - height: 80, - backgroundColor: "#fff4", - }, - text: { - color: semanticColors[ - `REDESIGN_BUTTON_${variant.toUpperCase()}_TEXT` - ], - textTransform: "uppercase", - }, - }); - - const shinyTranslate = Reanimated.useSharedValue(0); - const randomness = React.useRef( - 1500 + Math.floor(Math.random() * 696), - ).current; - - React.useEffect(() => { - if (width !== 0 && shiny) { - shinyTranslate.value = Reanimated.withRepeat( - Reanimated.withSequence( - Reanimated.withTiming(-width, { duration: 0 }), - Reanimated.withDelay( - randomness, - Reanimated.withTiming(width, { - duration: 800, - easing: Reanimated.Easing.inOut( - Reanimated.Easing.cubic, - ), - }), - ), - ), - 0, - ); - } else shinyTranslate.value = 500; - }, [shiny, width]); - - return ( - { - setWidth(layout.nativeEvent.layout.width * 0.5 + 4); - }} - > - {shiny && width !== 0 && ( - - )} - - {children} - - - ); -} diff --git a/nexxstuff/components/sheets/ChooseSheet.tsx b/nexxstuff/components/sheets/ChooseSheet.tsx deleted file mode 100644 index 65deeaa..0000000 --- a/nexxstuff/components/sheets/ChooseSheet.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { React, ReactNative as RN } from "@vendetta/metro/common"; -import { Forms } from "@vendetta/ui/components"; -import type { ImageSourcePropType } from "react-native"; - -import { ActionSheet } from "../ActionSheet"; - -const { FormRow } = Forms; - -export default function ChooseSheet({ - title, - value: _value, - options, - callback, -}: { - title: string; - value: string | number | boolean | null; - options: { - name: string; - description?: string; - value: typeof _value; - icon?: ImageSourcePropType; - iconColor?: any; - iconComponent?: React.ReactNode; - }[]; - callback: (v: typeof _value) => void; -}) { - const [value, setValue] = React.useState(_value); - - return ( - - {options.map((x) => ( - - )} - leading={} - onPress={() => { - setValue(x.value); - callback(x.value); - }} - /> - ))} - - ); -} diff --git a/nexxstuff/deps.ts b/nexxstuff/deps.ts deleted file mode 100644 index fc93f24..0000000 --- a/nexxstuff/deps.ts +++ /dev/null @@ -1,227 +0,0 @@ -import type * as _FlashList from "@shopify/flash-list"; -import { find, findByName, findByProps } from "@vendetta/metro"; -import { ReactNative as RN } from "@vendetta/metro/common"; -import type * as _Reanimated from "react-native-reanimated"; -import type { StateStorage } from "zustand/middleware"; - -// -// JS deps -// -export const WebView = find((x: any) => x?.WebView && !x.default) - .WebView as typeof import("react-native-webview").default; - -export const Svg = findByProps("SvgXml") as typeof import("react-native-svg"); - -export const Reanimated = findByProps("useSharedValue") as typeof _Reanimated; - -export const FlashList = findByProps("FlashList") - .FlashList as typeof _FlashList.FlashList; - -export const Joi = findByProps("isJoi") as unknown as typeof import("joi"); - -export const zustand = (findByProps("create", "useStore") || { - create: findByName("create"), -}) as typeof import("zustand"); - -export const zustandMW = findByProps( - "createJSONStorage", - "persist", -) as typeof import("zustand/middleware"); - -export const DocumentPicker = findByProps( - "pickSingle", - "isCancel", -) as typeof import("react-native-document-picker"); -export const DocumentsNew = findByProps( - "pick", - "saveDocuments", -) as typeof import("@react-native-documents/picker"); - -const _MAS = findByProps("MobileAudioSound").MobileAudioSound; - -// Messy code -export class MobileAudioSound { - // Events - public onPlay?: () => void; - public onStop?: () => void; - public onEnd?: () => void; - public onLoad?: (loaded: boolean) => void; - - private mas: any; - - public duration?: number; - public isLoaded?: boolean; - public isPlaying?: boolean; - - /** Discord when it comes to not changing everything every fucking version!!! */ - private get ensureSoundGetter() { - return this.mas?._ensureSound || this.mas?.ensureSound; - } - - /** Preloads the audio, which automatically makes us better than Discord because they DON'T do that for some reason */ - private async _preloadSound(skip?: boolean) { - const { _duration } = await this.ensureSoundGetter.bind(this.mas)(); - this.duration = RN.Platform.select({ - ios: _duration ? _duration * 1000 : _duration, - default: _duration, - }); - this.isLoaded = !!_duration; - - if (!skip) this.onLoad?.(!!_duration); - return !!_duration; - } - - constructor( - public url: string, - public usage: "notification" | "voice" | "ring_tone" | "media", - public volume: number, - events?: { - onPlay?: () => void; - onStop?: () => void; - onEnd?: () => void; - onLoad?: (loaded: boolean) => void; - }, - ) { - this.mas = new _MAS( - url, - { - media: "vibing_wumpus", - notification: "activity_launch", - ring_tone: "call_ringing", - voice: "mute", - }[usage], - volume, - "default", - ); - this.mas.volume = volume; - - this._preloadSound(); - for (const [key, val] of Object.entries(events ?? {})) this[key] = val; - } - - private _playTimeout?: number; - - /** Plays the audio */ - async play() { - if (!this.isLoaded && this.isLoaded !== false) { - await this._preloadSound(); - } - if (!this.isLoaded) return; - - this.mas.volume = this.volume; - await this.mas.play(); - this.isPlaying = true; - this.onPlay?.(); - - clearTimeout(this._playTimeout); - this._playTimeout = setTimeout( - () => (this.onEnd?.(), this.stop()), - this.duration, - ) as any; - } - - /** Stops the audio */ - async stop() { - if (!this.isLoaded) return; - - this.mas.stop(); - this.isPlaying = false; - this.onStop?.(); - - clearTimeout(this._playTimeout); - await this._preloadSound(true); - } -} - -// -// raw native modules -// - -// ripped straight from https://github.com/revenge-mod/revenge-bundle/blob/0a90d762e5c9996bb6613edd136fafe2b8f25908/src/lib/api/native/modules/index.ts -// why the fuck isnt that exported -const nmp = window.nativeModuleProxy; - -function getNativeModule(...names: string[]): T | undefined { - for (const name of names) { - if (globalThis.__turboModuleProxy) { - const module = globalThis.__turboModuleProxy(name); - if (module) return module as T; - } - - if (nmp[name]) return nmp[name] as T; - } - - return undefined; -} - -export const RNCacheModule = getNativeModule( - "NativeCacheModule", - "MMKVManager", -)!; - -export const RNChatModule = getNativeModule<{ - updateRows: (id: string, json: string) => any; -}>( - "NativeChatModule", - "DCDChatManager", -)!; - -export const RNFileModule = getNativeModule<{ - /** - * @param path **Full** path to file - */ - fileExists: (path: string) => Promise; - /** - * Allowed URI schemes on Android: `file://`, `content://` ([See here](https://developer.android.com/reference/android/content/ContentResolver#accepts-the-following-uri-schemes:_3)) - */ - getSize: (uri: string) => Promise; - /** - * @param path **Full** path to file - * @param encoding Set to `base64` in order to encode response - */ - readFile(path: string, encoding: "base64" | "utf8"): Promise; - saveFileToGallery?(uri: string, fileName: string, fileType: "PNG" | "JPEG"): Promise; - /** - * @param storageDir Either `cache` or `documents`. - * @param path Path in `storageDir`, parents are recursively created. - * @param data The data to write to the file - * @param encoding Set to `base64` if `data` is base64 encoded. - * @returns Promise that resolves to path of the file once it got written - */ - writeFile( - storageDir: "cache" | "documents", - path: string, - data: string, - encoding: "base64" | "utf8", - ): Promise; - /** - * Removes a file from the path given. - * (!) On Android, this always returns false, regardless if it fails or not! - * @param storageDir Either `cache` or `documents` - * @param path Path to the file to be removed - */ - removeFile(storageDir: "cache" | "documents", path: string): Promise; - /** - * Clear the folder from the path given - * (!) On Android, this only clears all *files* and not subdirectories! - * @param storageDir Either `cache` or `documents` - * @param path Path to the folder to be cleared - * @returns Whether the clearance succeeded - */ - clearFolder(storageDir: "cache" | "documents", path: string): Promise; - getConstants: () => { - /** - * The path the `documents` storage dir (see {@link writeFile}) represents. - */ - DocumentsDirPath: string; - CacheDirPath: string; - }; - /** - * Will apparently cease to exist some time in the future so please use {@link getConstants} instead. - * @deprecated - */ - DocumentsDirPath: string; -}>( - "NativeFileModule", - "DCDFileManager", -)!; diff --git a/nexxstuff/hooks/usePromise.ts b/nexxstuff/hooks/usePromise.ts deleted file mode 100644 index bff0648..0000000 --- a/nexxstuff/hooks/usePromise.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { React } from "@vendetta/metro/common"; - -type PromiseRet = - | { - fulfilled: false; - } - | { - fulfilled: true; - success: false; - error: any; - } - | { - fulfilled: true; - success: true; - response: Res; - }; - -export default function usePromise( - promise: (signal: AbortSignal) => Promise, - deps: any[] = [], -): PromiseRet { - const [res, setRes] = React.useState>({ - fulfilled: false, - }); - const tracker = React.useRef(0); - - React.useEffect(() => { - const id = Date.now(); - tracker.current = id; - - const controller = new AbortController(); - - setRes({ - fulfilled: false, - }); - promise(controller.signal) - .then( - response => - tracker.current === id - && setRes({ - fulfilled: true, - success: true, - response, - }), - ) - .catch( - error => - tracker.current === id - && setRes({ - fulfilled: true, - success: false, - error, - }), - ); - - return () => { - controller.abort(); - }; - }, deps); - - return res; -} diff --git a/nexxstuff/lib/intlProxy.ts b/nexxstuff/lib/intlProxy.ts deleted file mode 100644 index d0b879d..0000000 --- a/nexxstuff/lib/intlProxy.ts +++ /dev/null @@ -1,68 +0,0 @@ -// a drop-in replacement for i18n -// https://github.com/discord/discord-intl - -import { findByProps } from "@vendetta/metro"; -import { i18n } from "@vendetta/metro/common"; - -const { intl, t: intlMap } = findByProps("intl") ?? {}; -const { runtimeHashMessageKey } = findByProps("runtimeHashMessageKey") ?? {}; - -export function getLocale() { - const lang = intl?.currentLocale ?? i18n.getLocale?.() ?? "en"; - return lang; -} - -const isUsingi18n = !!i18n.Messages.DISCORD || !intl || !intlMap - || !runtimeHashMessageKey; - -const _messages = {} as Record; -const intlProxy = Object.freeze( - new Proxy(_messages, { - get(trg, prop, receiv) { - const key = prop.toString(); - if (key in _messages) return Reflect.get(trg, key, receiv); - - if (isUsingi18n) return (_messages[key] = i18n.Messages[key]); - - const hash = runtimeHashMessageKey(key); - try { - return (_messages[key] = intl.string(intlMap[hash])); - } catch (e) { - console.warn( - `intlProxy: Failed to get intl message for key: ${key} (${hash})`, - e, - ); - return (_messages[key] = ""); - } - }, - }), -); - -const _format = {} as Record) => string>; -export const intlFormat = Object.freeze( - new Proxy(_format, { - get(trg, prop, receiv) { - const key = prop.toString(); - if (key in _format) return Reflect.get(trg, key, receiv); - - if (isUsingi18n) { - return (_format[key] = typeof i18n.Messages[key] === "string" - ? () => i18n.Messages[key] - : i18n.Messages[key].format); - } - - const hash = runtimeHashMessageKey(key); - try { - return (_format[key] = values => intl.format(intlMap[hash], values)); - } catch (e) { - console.warn( - `intlFormat: Failed to get intl format message for key: ${key} (${hash})`, - e, - ); - return (_format[key] = () => ""); - } - }, - }), -); - -export default intlProxy; diff --git a/nexxstuff/lib/pinToSettings/index.ts b/nexxstuff/lib/pinToSettings/index.ts deleted file mode 100644 index ee8b9ba..0000000 --- a/nexxstuff/lib/pinToSettings/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { patchPanelUI } from "./panel"; -import { patchTabsUI } from "./tabs"; - -export interface PinToSettingsTabs { - key: string; - title: () => string; - icon?: number; - predicate?: () => boolean; - trailing?: () => React.ReactNode; - page: React.ComponentType; -} - -export function patchSettingsPin(tabs: PinToSettingsTabs): () => void { - const patches: (() => void)[] = []; - - let disabled = false; - - const realPredicate = tabs.predicate ?? (() => true); - tabs.predicate = () => (disabled ? false : realPredicate()); - - patchPanelUI(tabs, patches); - patchTabsUI(tabs, patches); - patches.push(() => (disabled = true)); - - return () => { - for (const x of patches) { - x(); - } - }; -} diff --git a/nexxstuff/lib/pinToSettings/panel.tsx b/nexxstuff/lib/pinToSettings/panel.tsx deleted file mode 100644 index 47e8580..0000000 --- a/nexxstuff/lib/pinToSettings/panel.tsx +++ /dev/null @@ -1,91 +0,0 @@ -// Based on: https://github.com/pyoncord/Bunny/blob/dev/src/lib/ui/settings/patches/panel.tsx - -import { logger } from "@vendetta"; -import { NavigationNative } from "@vendetta/metro/common"; -import { after } from "@vendetta/patcher"; -import { Forms } from "@vendetta/ui/components"; -import { findInReactTree } from "@vendetta/utils"; -import intlProxy from "../intlProxy"; -import type { PinToSettingsTabs } from "."; - -const { FormSection, FormRow } = Forms; - -function Section({ tabs }: { tabs: PinToSettingsTabs }) { - const navigation = NavigationNative.useNavigation(); - - return ( - } - trailing={ - <> - {tabs.trailing ? tabs.trailing : null} - - - } - // Based on: https://github.com/pyoncord/Bunny/blob/dev/src/lib/ui/settings/patches/shared.tsx - onPress={() => { - const Component = tabs.page; - - navigation.navigate("BUNNY_CUSTOM_PAGE", { - title: tabs.title(), - render: () => , - }); - }} - /> - ); -} - -export function patchPanelUI(tabs: PinToSettingsTabs, patches: (() => void)[]) { - const { bunny } = window as any; - - // panel settings were removed in version 264.5 💔 - try { - patches.push( - after( - "default", - bunny.metro.findByNameLazy( - "UserSettingsOverviewWrapper", - false, - ), - (_, ret) => { - const UserSettingsOverview = findInReactTree( - ret.props.children, - n => n.type?.name === "UserSettingsOverview", - ); - - patches.push( - after( - "render", - UserSettingsOverview.type.prototype, - (_args, res) => { - const titles = [ - intlProxy.BILLING_SETTINGS, - intlProxy.PREMIUM_SETTINGS, - ]; - - const sections = findInReactTree( - res.props.children, - n => n?.children?.[1]?.type === FormSection, - )?.children; - - if (sections) { - const index = sections.findIndex((c: any) => titles.includes(c?.props.label)); - - sections.splice( - -~index || 4, - 0, -
, - ); - } - }, - ), - ); - }, - true, - ), - ); - } catch { - logger.info("Panel UI patch failed graciously 💔"); - } -} diff --git a/nexxstuff/lib/pinToSettings/tabs.tsx b/nexxstuff/lib/pinToSettings/tabs.tsx deleted file mode 100644 index 89a08b0..0000000 --- a/nexxstuff/lib/pinToSettings/tabs.tsx +++ /dev/null @@ -1,105 +0,0 @@ -// Based on: https://github.com/pyoncord/Bunny/blob/dev/src/lib/ui/settings/patches/tabs.tsx - -import { findByProps } from "@vendetta/metro"; -import { after } from "@vendetta/patcher"; -import { findInReactTree } from "@vendetta/utils"; - -import type { PinToSettingsTabs } from "."; - -const { bunny } = window as any; - -const { TableRowIcon } = findByProps("TableRowIcon"); - -const tabsNavigationRef = bunny.metro.findByPropsLazy("getRootNavigationRef"); -const settingConstants = bunny.metro.findByPropsLazy("SETTING_RENDERER_CONFIG"); -const createListModule = bunny.metro.findByPropsLazy("createList"); -const SettingsOverviewScreen = bunny.metro.findByNameLazy( - "SettingsOverviewScreen", - false, -); - -export function patchTabsUI(tabs: PinToSettingsTabs, patches: (() => void)[]) { - const row = { - [tabs.key]: { - type: "pressable", - title: tabs.title, - icon: tabs.icon, - IconComponent: tabs.icon && (() => ), - usePredicate: tabs.predicate, - useTrailing: tabs.trailing, - onPress: () => { - const navigation = tabsNavigationRef.getRootNavigationRef(); - const Component = tabs.page; - - navigation.navigate("BUNNY_CUSTOM_PAGE", { - title: tabs.title(), - render: () => , - }); - }, - withArrow: true, - }, - }; - - let rendererConfigValue = settingConstants.SETTING_RENDERER_CONFIG; - - Object.defineProperty(settingConstants, "SETTING_RENDERER_CONFIG", { - enumerable: true, - configurable: true, - get: () => ({ - ...rendererConfigValue, - ...row, - }), - set: v => (rendererConfigValue = v), - }); - - const firstRender = Symbol("pinToSettings meow meow"); - - try { - if (!createListModule) return; - patches.push( - after("createList", createListModule, (args) => { - // shrug?? - if (!args[0][firstRender]) { - args[0][firstRender] = true; - - const [config] = args; - const sections = config.sections; - - const section = sections?.find((x: any) => - ["Bunny", "Revenge", "Kettu"].some( - mod => x.label === mod && x.title === mod, - ) - ); - - if (section?.settings) { - section.settings = [...section.settings, tabs.key]; - } - } - }), - ); - } catch { - if (!SettingsOverviewScreen) return; - patches.push( - after("default", SettingsOverviewScreen, (args, ret) => { - // shrug?? - if (!args[0][firstRender]) { - args[0][firstRender] = true; - - const { sections } = findInReactTree( - ret, - i => i.props?.sections, - ).props; - const section = sections?.find((x: any) => - ["Bunny", "Revenge", "Kettu"].some( - mod => x.label === mod && x.title === mod, - ) - ); - - if (section?.settings) { - section.settings = [...section.settings, tabs.key]; - } - } - }), - ); - } -} diff --git a/nexxstuff/lib/redesign/index.tsx b/nexxstuff/lib/redesign/index.tsx deleted file mode 100644 index 077ffdd..0000000 --- a/nexxstuff/lib/redesign/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { findByProps } from "@vendetta/metro"; -import { React, ReactNative as RN } from "@vendetta/metro/common"; -import { showToast } from "@vendetta/ui/toasts"; - -import type * as t from "./types"; // shamelessly stolen from Bunny - -const NotFound = (prop: string, isFunction?: boolean) => () => { - const trigger = () => { - showToast(`${prop} not found! Search for PNF in Debug Logs!`); - console.warn( - `!! PNF ERROR !!\nMissing the redesign ${ - isFunction ? "function" : "component" - }: ${prop}. Please bug @nexpid about this on Discord!`, - ); - }; - - if (isFunction) trigger(); - else React.useEffect(trigger, []); - - return null; -}; - -const findProp = (...props: string[]) => findByProps(...props)?.[props[0]]; -const findPropPolyfill = (isFunction: boolean, ...props: string[]) => - findProp(...props) ?? NotFound(props[0], isFunction); - -// components - -// buttons -export const Button = findPropPolyfill(false, "Button") as t.Button; -export const RowButton = findPropPolyfill(false, "RowButton") as t.RowButton; -export const FloatingActionButton = findPropPolyfill( - false, - "FloatingActionButton", -) as t.FAB; -export const PressableScale = findPropPolyfill( - false, - "PressableScale", -) as typeof RN.Pressable; -export const IconButton = findPropPolyfill(false, "IconButton") as t.IconButton; -export const ContextMenu = findPropPolyfill( - false, - "ContextMenu", -) as t.ContextMenu; - -// inputs -const _Slider = findPropPolyfill(false, "Slider"); -export const Slider = - (props => ( - - <_Slider - {...props} - onValueChange={(val: any) => props.value !== val && props.onValueChange?.(val)} - /> - - )) as t.Slider; -export const TextInput = findPropPolyfill(false, "TextInput") as t.TextInput; - -// tabs -export const Tabs = findPropPolyfill(false, "Tabs") as t.Tabs; -export const SegmentedControlPages = findPropPolyfill( - false, - "SegmentedControlPages", -) as t.Tabs; - -// views -export const Card = findPropPolyfill(false, "Card") as t.Card; -export const Stack = findPropPolyfill(false, "Stack") as t.Stack; - -// functions - -export const useSegmentedControlState = findPropPolyfill( - true, - "useSegmentedControlState", -) as t.useSegmentedControlState; diff --git a/nexxstuff/lib/redesign/types.ts b/nexxstuff/lib/redesign/types.ts deleted file mode 100644 index dc05547..0000000 --- a/nexxstuff/lib/redesign/types.ts +++ /dev/null @@ -1,175 +0,0 @@ -import type { - FlexAlignType, - ImageSourcePropType, - PressableProps, - TextInputProps, - ViewProps, -} from "react-native"; -import type { SharedValue } from "react-native-reanimated"; - -export type ButtonVariant = - | "primary" - | "secondary" - | "tertiary" - | "primary-overlay" - | "secondary-overlay" - | "destructive" - | "active"; - -export interface PrimitiveButton { - onPress?: () => void; - disabled?: boolean; - icon?: ImageSourcePropType | React.ReactNode; - style?: ViewProps["style"]; -} -export interface PrimitiveButtonIcon { - source: ImageSourcePropType; -} - -export type Button = - & React.FC< - PrimitiveButton & { - variant?: ButtonVariant; - size?: "sm" | "md" | "lg"; - text: string; - iconPosition?: "start" | "end"; - grow?: boolean; - loading?: boolean; - onPressIn?: () => void; - onPressOut?: () => void; - } - > - & { - Icon: React.FC; - }; - -export type RowButton = - & React.FC< - PrimitiveButton & { - icon?: ImageSourcePropType; - label: string; - subLabel?: string; - variant?: "primary" | "secondary"; - trailing?: React.ReactNode; - disabled?: boolean; - arrow?: boolean; - /** Requires onPress to bet set */ - onPressIn?: () => void; - /** Requires onPress to bet set */ - onPressOut?: () => void; - } - > - & { - Icon: React.FC< - PrimitiveButtonIcon & { - variant?: "secondary" | "danger" | "blurple" | "boosting-pink"; - } - >; - }; - -export type FAB = React.FC<{ - icon: ImageSourcePropType; - onPress?: () => void; - text?: string; - positionBottom?: number; - state?: { - collapseText?: SharedValue; - }; -}>; - -export type IconButton = React.FC<{ - icon: ImageSourcePropType | React.ReactNode; - onPress?: () => void; - onLongPress?: () => void; - loading?: boolean; - disabled?: boolean; - size?: "sm" | "md" | "lg"; - variant?: ButtonVariant; - style?: ViewProps["style"]; -}>; - -export type ContextMenu = React.FC<{ - title?: string; - triggerOnLongPress?: boolean; - align?: "left" | "right" | "above" | "below"; - items: { - label: string; - variant: "default" | "destructive"; - action: () => void; - iconSource?: ImageSourcePropType; - // TODO add IconComponent - }[]; - children: (data: { - ref: React.MutableRefObject; - onPress: (() => void) | undefined; - onLongPress: (() => void) | undefined; - onAccessibilityAction: () => void; - accessibilityActions: { name: string; label: string }[]; - }) => React.ReactNode | undefined; -}>; - -export type Slider = React.FC<{ - value: number; - accessibilityLabel?: string; - accessibilityValue?: { - text: string; - }; - step: number; - onValueChange?: (value: number) => void; - minimumValue: number; - maximumValue: number; - onSlidingStart?: () => void; - onSlidingComplete?: () => void; - startIcon?: React.ReactNode; - endIcon?: React.ReactNode; -}>; - -export type TextInput = React.FC< - Omit & { - onChange?: (value: string) => void; - size?: "sm" | "md" | "lg"; - label?: string; - description?: React.ReactNode; - focusable?: boolean; - isDisabled?: boolean; - leadingPressableProps?: PressableProps; - leadingIcon?: React.FC; - leadingText?: string; - trailingPressableProps?: PressableProps; - trailingIcon?: React.FC; - trailingText?: string; - isClearable?: boolean; - status?: "error" | "default"; - errorMessage?: string; - isCentered?: boolean; - grow?: boolean; - isRound?: boolean; - ref?: any; - } ->; - -type controlState = symbol; -export type Tabs = React.FC<{ - state: controlState; -}>; - -export type Card = React.FC; - -export type Stack = React.FC< - React.PropsWithChildren<{ - direction?: "horizontal" | "vertical"; - spacing?: number; - align?: FlexAlignType; - style?: ViewProps["style"]; - }> ->; - -export type useSegmentedControlState = (state: { - defaultIndex: number; - items: { - label: string; - id: string; - page: React.ReactNode; - }[]; - pageWidth: number; -}) => controlState; diff --git a/nexxstuff/lib/ui.ts b/nexxstuff/lib/ui.ts deleted file mode 100644 index b3242dd..0000000 --- a/nexxstuff/lib/ui.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NavigationNative, React } from "@vendetta/metro/common"; - -export function managePage( - options: { - title?: string; - headerLeft?: React.FC; - headerRight?: React.FC; - }, - navigation?: any, - ...deps: any[] -) { - if (!navigation) navigation = NavigationNative.useNavigation(); - - React.useEffect(() => { - navigation.setOptions(options); - }, [navigation, ...deps]); -} diff --git a/nexxstuff/migration.tsx b/nexxstuff/migration.tsx deleted file mode 100644 index 7421597..0000000 --- a/nexxstuff/migration.tsx +++ /dev/null @@ -1,294 +0,0 @@ -import { React, ReactNative } from "@vendetta/metro/common"; -import { installPlugin, plugins, removePlugin } from "@vendetta/plugins"; -import { createMMKVBackend } from "@vendetta/storage"; -import { showConfirmationAlert } from "@vendetta/ui/alerts"; -import { getAssetIDByName } from "@vendetta/ui/assets"; -import { showToast } from "@vendetta/ui/toasts"; -import { RichText } from "./components/RichText"; -import Text from "./components/Text"; -import { Reanimated, RNFileModule } from "./deps"; -import { Stack } from "./lib/redesign"; - -const { Pressable } = ReactNative; - -interface InvalidDomain { - match: RegExp; - label: string; -} - -const specialIDs = { - usrpfp: "userpfp", -} as Record; -const allowedPluginIDs = [ - PLUGINS_LIST, - Object.keys(specialIDs), -].flat(); - -const invalidDomains: InvalidDomain[] = [{ - match: /vendetta\.nexpid\.xyz\/(?.+)\/$/i, - label: "vendetta.nexpid.xyz", -}, { - match: /dev\.bunny\.nexpid\.xyz\/(?.+)\/$/i, - label: "dev.bunny.nexpid.xyz", -}, { - match: /bunny\.nexpid\.xyz\/(?.+)\/$/i, - label: "bunny.nexpid.xyz", -}]; - -const migrationLockFilePath = "nexpid/revengemigration.txt"; - -function MigrationModal( - { affectedPlugin, domains }: { - affectedPlugin?: string; - domains: string[]; - }, -) { - const jumper = Reanimated.useSharedValue(0); - const spinner = Reanimated.useSharedValue("0deg"); - const squisherX = Reanimated.useSharedValue(1); - const squisherY = Reanimated.useSharedValue(1); - const [toggle, setToggle] = React.useState(true); - - const duration = 300; - const fromY = -10; - const toY = 30; - const spinBetween = 11; - - const squishConfig = { duration: 0 }; - const squishVal = 1.25; - const releaseConfig = { - duration: duration * 2, - easing: Reanimated.Easing.out(Reanimated.Easing.quad), - }; - const releaseVal = 0.9; - - React.useEffect(() => { - if (!toggle) { - jumper.value = 0; - spinner.value = "0deg"; - squisherX.value = 1; - squisherY.value = 1; - return; - } - - jumper.value = Reanimated.withRepeat( - Reanimated.withSequence( - Reanimated.withTiming(toY, { - easing: Reanimated.Easing.out(Reanimated.Easing.quad), - duration, - }), - Reanimated.withTiming(fromY, { - easing: Reanimated.Easing.quad, - duration, - }), - ), - -1, - ); - spinner.value = Reanimated.withRepeat( - Reanimated.withSequence( - Reanimated.withTiming("0deg", { duration: (duration * 2) * spinBetween }), - Reanimated.withTiming("-360deg", { - duration: duration * 2, - easing: Reanimated.Easing.out(Reanimated.Easing.exp), - }), - ), - -1, - ); - - squisherX.value = Reanimated.withRepeat( - Reanimated.withSequence( - Reanimated.withTiming(squishVal, squishConfig), - Reanimated.withTiming(releaseVal, releaseConfig), - ), - -1, - ); - squisherY.value = Reanimated.withRepeat( - Reanimated.withSequence( - Reanimated.withTiming(1 / squishVal, squishConfig), - Reanimated.withTiming(1 / releaseVal, releaseConfig), - ), - -1, - ); - }, [toggle]); - - return ( - - - setToggle(!toggle)} - accessibilityRole="togglebutton" - accessibilityLabel="toggle jumping animation" - > - - - - - Hey, it's me, nexpid! - - - You might know me. I made {affectedPlugin - ? ( - <> - a plugin you use, {affectedPlugin} - - ) - : "some of the plugins you use"}! - - - - - {affectedPlugin - ? "The plugin you use has" - : "Some of my plugins you use have"} one of these domains: - - - {domains.map(label => `• ${label}`).join("\n")} - - - These domains are being deprecated and plugins on them won't receive any new updates. Please - use the new domain instead: - - - revenge.nexpid.xyz - - - ); -} - -// thanks to the shim, this only gets run once even if multiple plugins are installed -export async function runMigration() { - if ( - await RNFileModule.fileExists( - `${RNFileModule.DocumentsDirPath}/${migrationLockFilePath}`, - ) - ) return; - - const affected: { - id: string; - link: string; - name: string; - }[] = []; - const isOutdated = new Set(); - for (const plugin of Object.keys(plugins)) { - const parser = invalidDomains.find(x => x.match.test(plugin)); - const id = parser && plugin.match(parser.match)?.groups?.plugin; - - if (id && allowedPluginIDs.includes(id)) { - affected.push({ - id, - link: plugin, - name: plugins[plugin].manifest.name, - }); - isOutdated.add(parser.label); - - if (plugin.includes("bn-plugins.github.io/vd-proxy")) { - isOutdated.add("proxied plugin links"); - } - } - } - - if (isOutdated.size) { - showConfirmationAlert({ - // @ts-expect-error Missing from typings - children: ( - - ), - confirmText: "Migrate now", - async onConfirm() { - showToast("Starting migration"); - - console.log("MIGRATION STARTS HERE, AFFECTED", affected); - try { - // take all plugins that are bad & make good :blush: - for (const { id, link, name } of affected) { - const newPlugin = `https://revenge.nexpid.xyz/${specialIDs[id] ?? id}/`; - console.log( - "FROM", - link, - "TO", - newPlugin, - ); - - await createMMKVBackend(newPlugin).set(await createMMKVBackend(link).get()); - if (!plugins[newPlugin]) await installPlugin(newPlugin); - // returns a promise (bug in types) - await removePlugin(link); - showToast(`Migrated ${name}`); - } - console.log("MIGRATION ENDS HERE"); - } catch (e) { - console.error("Migration error", e); - return showToast( - `Got an error! ${e} (ping nexpid about this!!!)`, - getAssetIDByName("CircleXIcon-primary"), - ); - } - - showToast( - "Migrated successfully! Please reload your client to apply changes", - getAssetIDByName("CircleCheckIcon-primary"), - ); - }, - cancelText: "Remind me later", - secondaryConfirmText: "I understand (don't show again)", - onConfirmSecondary() { - RNFileModule.writeFile("documents", migrationLockFilePath, "revenged", "utf8"); - }, - }); - } -} diff --git a/nexxstuff/types.tsx b/nexxstuff/types.tsx deleted file mode 100644 index ede70be..0000000 --- a/nexxstuff/types.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { findByProps, findByStoreName } from "@vendetta/metro"; -import { FluxDispatcher, ReactNative as RN } from "@vendetta/metro/common"; -import { getAssetIDByName } from "@vendetta/ui/assets"; -import { showToast } from "@vendetta/ui/toasts"; -import type { StyleSheet } from "react-native"; - -import { plugin } from "@vendetta"; -import { before } from "@vendetta/patcher"; -import type Modal from "./components/Modal"; -import { RNChatModule } from "./deps"; -import type { ChatRowTypeMessage } from "./typings"; - -const ThemeStore = findByStoreName("ThemeStore"); -const { triggerHaptic } = findByProps("triggerHaptic"); - -const colorModule = findByProps("colors", "unsafe_rawColors"); -const colorResolver = colorModule?.internal ?? colorModule?.meta; - -const { useThemeContext } = findByProps("useThemeContext"); - -export const TextStyleSheet = findByProps("TextStyleSheet") - .TextStyleSheet as _TextStyleSheet; - -export const Navigator = findByProps("Navigator")?.Navigator; -export const modalCloseButton = findByProps( - "getHeaderCloseButton", -)?.getHeaderCloseButton; -export const { popModal, pushModal } = findByProps("popModal", "pushModal"); - -export type Entries = [keyof T, T[keyof T]]; - -// ... -export const getDiscordTheme = () => { - // getDefaultFallbackTheme is not exported :( - const { theme } = ThemeStore; - - if (theme.startsWith("vd-theme-")) return theme.split("-")[3]; - return theme; -}; - -export const resolveCustomSemantic = (dark: any, light: any, inverted?: boolean) => - getDiscordTheme() === "light" ? (inverted ? dark : light) : (inverted ? light : dark); - -export const lerp = (og: string, target: string, perc: number) => { - const hex2rgb = (hex: string) => - hex.match(/\w\w/g)?.map(x => Number.parseInt(x, 16)) ?? [0, 0, 0]; - const rgb2hex = (rgb: number[]) => `#${rgb.map(x => x.toString(16).padStart(2, "0")).join("")}`; - - const ogR = hex2rgb(og); - const targetR = hex2rgb(target); - - const result = ogR.map((ogC, i) => Math.round(ogC + (targetR[i] - ogC) * perc)); - - return rgb2hex(result); -}; - -export function resolveSemanticColor( - color: any, - theme: string = ThemeStore.theme, -) { - return (color && colorResolver?.resolveSemanticColor(theme, color)) || "#000000"; -} - -export function getUserAvatar( - user: { - discriminator: string; - avatar?: string; - id: string; - }, - animated?: boolean, -): string { - const isPomelo = user.discriminator === "0"; - - return user.avatar - ? `https://cdn.discordapp.com/avatars/${user.id}/${ - animated && user.avatar.startsWith("a_") - ? `${user.avatar}.gif` - : `${user.avatar}.png` - }` - : `https://cdn.discordapp.com/embed/avatars/${ - isPomelo - ? (Number.parseInt(user.id) >> 22) % 6 - : Number.parseInt(user.discriminator) % 5 - }`; -} - -export function openModal(key: string, modal: typeof Modal) { - const empty = Symbol("empty"); - if (!Navigator || !modalCloseButton) { - showToast( - `${ - [ - Navigator ? empty : "Navigator", - modalCloseButton ? empty : "modalCloseButton", - ] - .filter(x => x !== empty) - .join(", ") - } is missing! Please try reinstalling your client.`, - getAssetIDByName("CircleXIcon-primary"), - ); - return; - } - - pushModal({ - key, - modal: { - key, - modal, - animation: "slide-up", - shouldPersistUnderModals: false, - closable: true, - }, - }); -} - -export function doHaptic(dur: number): Promise { - triggerHaptic(); - const interval = setInterval(() => triggerHaptic(), 1); - return new Promise(res => setTimeout(() => res(clearInterval(interval)), dur)); -} - -export function androidifyColor(color: string, alpha = 255): number { - const [_, r, g, b] = color.match(/#([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})/i) ?? []; - if (!_) return 0; - - return ( - ((alpha & 0xff) << 24) - | ((Number.parseInt(r, 16) & 0xff) << 16) - | ((Number.parseInt(g, 16) & 0xff) << 8) - | (Number.parseInt(b, 16) & 0xff) - ); -} - -export function fluxSubscribe( - topic: string, - callback: (data: any) => void, - once?: boolean, -) { - const cback = (data: any) => ( - callback(data), once && FluxDispatcher.unsubscribe(topic, cback) - ); - FluxDispatcher.subscribe(topic, cback); - return () => FluxDispatcher.unsubscribe(topic, cback); -} - -export function formatBytes(bytes: number) { - const sizes = ["B", "KB", "MB", "GB", "TB"]; - if (bytes === 0) return "0 B"; - - const i = Math.floor(Math.log(bytes) / Math.log(1024)); - return `${(bytes / 1024 ** i).toFixed(2)} ${sizes[i]}`; -} - -export function deepEquals(x: any, y: any) { - if (x === y) return true; - if (typeof x === "object" && typeof y === "object" && x && y) { - if (Object.keys(x).length !== Object.keys(y).length) return false; - - for (const prop in x) { - if (Object.hasOwn(y, prop)) { - if (!deepEquals(x[prop], y[prop])) return false; - } else return false; - } - - return true; - } -} - -export function formatDuration(duration: number) { - const seconds = duration % 60; - const minutes = Math.floor(duration / 60) % 60; - const hours = Math.floor(duration / 3600); - - if (hours > 0) { - return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`; - } - return `${minutes}:${String(seconds).padStart(2, "0")}`; -} - -export function createThemeContextStyleSheet< - T extends StyleSheet.NamedStyles, ->(sheet: T) { - const themeContext = useThemeContext(); - - for (const key in sheet) { - // @ts-expect-error types - sheet[key] = new Proxy(RN.StyleSheet.flatten(sheet[key]), { - get(target, prop, receiver) { - const res = Reflect.get(target, prop, receiver); - return colorResolver.isSemanticColor(res) - ? resolveSemanticColor(res, themeContext?.theme) - : res; - }, - }); - } - - return sheet; -} - -export function patchRows(callback: (rows: ChatRowTypeMessage[]) => void) { - return before("updateRows", RNChatModule, (args) => { - const rows = JSON.parse(args[1]); - try { - callback(rows); - } catch (e: any) { - console.error(`[nexpid.patchRows][${plugin.id}]`, e.stack); - } - - args[1] = JSON.stringify(rows); - }); -} - -// ... - -export type TextStyleSheetCase = "normal" | "medium" | "semibold" | "bold"; - -type TextStyleSheetHasExtraBoldCase = - | "heading-sm" - | "heading-md" - | "heading-lg" - | "heading-xl" - | "heading-xxl" - | "heading-deprecated-12"; -export type TextStyleSheetHasCase = - | TextStyleSheetHasExtraBoldCase - | "text-xxs" - | "text-xs" - | "text-sm" - | "text-md" - | "text-lg" - | "redesign/message-preview" - | "redesign/channel-title"; - -export type TextStyleSheetVariant = - | `${TextStyleSheetHasCase}/${TextStyleSheetCase}` - | `${TextStyleSheetHasExtraBoldCase}/${TextStyleSheetCase | "extrabold"}` - | "eyebrow" - | "redesign/heading-18/bold" - | "display-sm" - | "display-md" - | "display-lg"; - -type _TextStyleSheet = Record< - TextStyleSheetVariant, - { - fontSize: number; - lineHeight: number; - textTransform: "none" | "capitalize" | "uppercase" | "lowercase"; - fontFamily: string; - includeFontPadding: boolean; - letterSpacing?: number; - } ->; - -export interface SearchContext { - type: string; - [key: PropertyKey]: any; -} - -export function isObject(x: Record) { - return typeof x === "object" && !Array.isArray(x); -} diff --git a/nexxstuff/typings.d.ts b/nexxstuff/typings.d.ts deleted file mode 100644 index c8d1b1f..0000000 --- a/nexxstuff/typings.d.ts +++ /dev/null @@ -1,177 +0,0 @@ -export interface DisplayProfileData { - displayProfile: { - userId: string; - guildId?: string; - banner?: string; - bio?: string; - pronouns: string; - accentColor?: number; - emoji?: any; - themeColors?: number[]; - popoutAnimationParticleType?: any; - _profileThemesExperimentBucket?: number; - _guildMemberProfile?: GuildMemberProfile; - }; - user: User; -} - -export interface User { - hasFlag: (flag: any) => boolean; - isStaff: () => boolean; - isStaffPersonal: () => boolean; - hasAnyStaffLevel: () => boolean; - id: string; - username: string; - discriminator: string; - avatar?: string; - avatarDecoration?: string; - email?: string; - verified: boolean; - bot: boolean; - system: boolean; - mfaEnabled: boolean; - mobile: boolean; - desktop: boolean; - premiumType?: number; - flags: number; - publicFlags: number; - purchasedFlags: number; - premiumUsageFlags: number; - phone?: string; - nsfwAllowed?: boolean; - guildMemberAvatars: Record; - hasBouncedEmail: boolean; // ??? - personalConnectionId?: any; // ??? - globalName: string; -} - -export interface GuildMemberProfile { - userId: string; - guildId: string; - avatar?: string; - banner?: string; - accentColor?: number; - emoji?: any; - themeColors?: number[]; - popoutAnimationParticleType?: any; - bio?: string; - pronouns?: string; - badges: any[]; -} - -export interface ReactionEvent { - channelId: string; - messageId: string; - userId: string; - emoji: { name: string; id?: string; animated?: boolean }; - burst: boolean; - colors: string[]; - messageAuthorId: string; -} - -type VendettaSysColor = [ - string, - string, - string, - string, - string, - string, - string, - string, - string, - string, - string, - string, - string, -]; -export type VendettaSysColors = Record< - "neutral1" | "neutral2" | "accent1" | "accent2" | "accent3", - VendettaSysColor ->; - -type PlusColorResolvable = - | string - | [string, string | undefined, string | undefined, string | undefined]; -export interface PlusStructureV0 { - icons?: Record; - mentionLineColor?: PlusColorResolvable; - customOverlays?: boolean; - iconpack?: string; - version: 0; -} - -export type PlusStructure = PlusStructureV0; - -export type ThemeWithPlus = Theme & { - data: ThemeDataWithPlus; -}; -export type ThemeDataWithPlus = ThemeData & { - plus?: PlusStructure; -}; - -// chat row stuff -export type ContentRow = { - type: "text"; - content: string; -} | { - type: - | "strong" - | "s" - | "italic" - | "em" - | "underline" - | "u" - | "strikethrough" - | "subtext"; - content: ContentRow[]; -} | { - type: "spoiler"; - channelId: string; - content: ContentRow[]; -} | { - type: "heading"; - level: number; - content: ContentRow[]; -} | { - type: "link"; - target: string; - content: ContentRow[]; -} | { - type: "list"; - ordered: boolean; - start?: number; - items: ContentRow[]; -} | { - type: "emoji"; - content: string; - surrogate: string; - jumboable?: boolean; -} | { - type: "customEmoji"; - id: string; - alt: string; - src: string; - frozenSrc: string; - jumboable?: boolean; -}; -export type ContentRowIs = Extract; - -interface MessageEmbed { - url: string; -} - -export interface ChatRowTypeMessage { - type: 1; - message: { - id: string; - authorId: string; - channelId: string; - guildId?: string; - content?: ContentRow[]; - embeds?: MessageEmbed[]; - codedLinks?: any[]; - communicationDisabled?: boolean; - }; -} - -export type ChatRow = ChatRowTypeMessage; diff --git a/tsconfig.json b/tsconfig.json index 99ff4e2..da4b292 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,10 +7,6 @@ "esModuleInterop": true, "jsx": "react-native", "allowJs": true, - "baseUrl": ".", - "paths": { - "@nlib/*": ["nexxstuff/*"] - } }, "include": [ "plugins",