diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/README.md b/src/equicordplugins/fakeProfileThemesAndEffects/README.md deleted file mode 100644 index 91ceedbea..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# FakeProfileThemesAndEffects - -Expanded FakeProfileThemes with effects diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/Builder.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/Builder.tsx deleted file mode 100644 index 2a735c528..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/Builder.tsx +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { copyWithToast } from "@utils/misc"; -import type { Guild } from "@vencord/discord-types"; -import { Button, showToast, Switch, UserStore, useState, useToken } from "@webpack/common"; - -import { buildFPTE } from "../lib/fpte"; -import { useAccentColor, usePrimaryColor, useProfileEffect, useShowPreview } from "../lib/profilePreview"; -import { BuilderButton, BuilderColorButton, CustomizationSection, openProfileEffectModal, tokens, useAvatarColors } from "."; - -export interface BuilderProps { - guild?: Guild | undefined; -} - -export function Builder({ guild }: BuilderProps) { - const [primaryColor, setPrimaryColor] = usePrimaryColor(null); - const [accentColor, setAccentColor] = useAccentColor(null); - const [effect, setEffect] = useProfileEffect(null); - const [preview, setPreview] = useShowPreview(true); - const [buildLegacy, setBuildLegacy] = useState(false); - - const avatarColors = useAvatarColors( - UserStore.getCurrentUser()?.getAvatarURL(guild?.id, 80), - useToken(tokens.unsafe_rawColors.PRIMARY_530 as any).hex(), - false - ); - - return ( - <> - -
- - - -
- - -
-
-
- - FPTE Builder Preview - - - Build backwards compatible FPTE - - - ); -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx deleted file mode 100644 index 45ffe1416..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderButton.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { Text, Tooltip } from "@webpack/common"; -import type { ComponentProps, RefObject } from "react"; - -export interface BuilderButtonProps { - label?: string | undefined; - tooltip?: string | undefined; - selectedStyle?: ComponentProps<"div">["style"]; - buttonProps?: ComponentProps<"div"> | undefined; - buttonRef?: RefObject; -} - -export const BuilderButton = ({ buttonRef, label, tooltip, selectedStyle, buttonProps }: BuilderButtonProps) => ( - - {tooltipProps => ( -
-
- {!selectedStyle && ( - - - - )} -
- {!!label && ( - - {label} - - )} -
- )} -
-); diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderColorButton.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderColorButton.tsx deleted file mode 100644 index aa669e876..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/BuilderColorButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { Popout, useRef } from "@webpack/common"; - -import { BuilderButton, type BuilderButtonProps, CustomColorPicker, type CustomColorPickerProps } from "."; - -export interface BuilderColorButtonProps extends Pick, Pick { - color: number | null; - setColor: (color: number | null) => void; -} - -export function BuilderColorButton({ label, color, setColor, suggestedColors }: BuilderColorButtonProps) { - const buttonRef = useRef(null); - return ( - ( - - )} - > - {popoutProps => { - const hexColor = color ? "#" + color.toString(16).padStart(6, "0") : undefined; - - return ( - - ); - }} - - ); -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/index.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/index.tsx deleted file mode 100644 index 07b8db36f..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/index.tsx +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { type ModalProps, openModal } from "@utils/modal"; -import type { Guild, useToken } from "@vencord/discord-types"; -import { extractAndLoadChunksLazy, findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import type { ComponentType, FunctionComponent, PropsWithChildren, ReactNode } from "react"; - -import type { ProfileEffectConfig } from "../lib/profileEffects"; - -export * from "./Builder"; -export * from "./BuilderButton"; -export * from "./BuilderColorButton"; -export * from "./settingsAboutComponent"; - -export interface CustomizationSectionProps extends PropsWithChildren { - borderType?: FeatureBorderType | undefined; - className?: string | undefined; - description?: ReactNode; - disabled?: boolean | undefined /* = false */; - errors?: string[] | undefined; - forcedDivider?: boolean | undefined /* = false */; - hasBackground?: boolean | undefined /* = false */; - hideDivider?: boolean | undefined /* = false */; - showBorder?: boolean | undefined /* = false */; - showPremiumIcon?: boolean | undefined /* = false */; - title?: ReactNode; - titleIcon?: ReactNode; - titleId?: string | undefined; -} - -// Original name: FeatureBorderTypes -export const enum FeatureBorderType { - LIMITED = "limited", - PREMIUM = "premium", -} - -export const CustomizationSection: ComponentType - = findComponentByCodeLazy(".customizationSectionBackground"); - -export const tokens: { - unsafe_rawColors: Record[0]>; -} = findByPropsLazy("unsafe_rawColors", "modules"); - -export const useAvatarColors: ( - avatarURL: string | null | undefined, - fallbackColor: string, - desaturateColors?: boolean | undefined /* = true */ -) => [string, string, ...string[]] = findByCodeLazy(".palette[", ".desaturateUserColors"); - -export interface CustomColorPickerProps { - className?: string | undefined; - eagerUpdate?: boolean | undefined /* = false */; - footer?: ReactNode; - middle?: ReactNode; - onChange: (color: number) => void; - onClose?: (() => void) | undefined; - showEyeDropper?: boolean | undefined /* = false */; - suggestedColors?: string[] | null | undefined; - wrapperComponentType?: ComponentType | null | undefined; - value?: string | number | null | undefined; -} - -export const CustomColorPicker = findComponentByCodeLazy(".customColorPicker"); - -interface ProfileEffectModalProps extends ModalProps { - analyticsLocations?: string[] | undefined; - guild?: Guild | null | undefined; - initialSelectedEffectId?: string | undefined; - onApply: (effect: ProfileEffectConfig | null) => void; -} - -let ProfileEffectModal: FunctionComponent = () => null; - -export function setProfileEffectModal(comp: typeof ProfileEffectModal) { - ProfileEffectModal = comp; -} - -const requireProfileEffectModal = extractAndLoadChunksLazy([/initialSelectedEffect:\i,analyticsLocations:/]); - -export async function openProfileEffectModal( - initialEffectId: ProfileEffectModalProps["initialSelectedEffectId"], - onApply: ProfileEffectModalProps["onApply"], - guild?: ProfileEffectModalProps["guild"] -) { - await requireProfileEffectModal(); - openModal(modalProps => ( - - )); -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/components/settingsAboutComponent.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/components/settingsAboutComponent.tsx deleted file mode 100644 index 7f8b1f581..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/components/settingsAboutComponent.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { Margins } from "@utils/margins"; -import { Forms } from "@webpack/common"; - -export const settingsAboutComponent = () => ( - - Usage - - After enabling this plugin, you will see custom theme colors and effects in the profiles of others using this plugin. -
- To set your own profile theme colors and effect: -
-
    -
  1. Go to your profile settings
  2. -
  3. Use the FPTE Builder to choose your profile theme colors and effect
  4. -
  5. Click the "Copy FPTE" button
  6. -
  7. Paste the invisible text anywhere in your About Me
  8. -
-
-
-); diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/index.tsx b/src/equicordplugins/fakeProfileThemesAndEffects/index.tsx deleted file mode 100644 index c27f9d976..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/index.tsx +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { definePluginSettings, Settings } from "@api/Settings"; -import { EquicordDevs } from "@utils/constants"; -import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; -import definePlugin, { OptionType } from "@utils/types"; -import { showToast, Toasts, useMemo } from "@webpack/common"; - -import { Builder, type BuilderProps, setProfileEffectModal, settingsAboutComponent } from "./components"; -import { ProfileEffectRecord, ProfileEffectStore } from "./lib/profileEffects"; -import { profilePreviewHook } from "./lib/profilePreview"; -import { decodeAboutMeFPTEHook } from "./lib/userProfile"; - -function replaceHelper( - string: string, - replaceArgs: readonly (readonly [searchRegExp: RegExp, replaceString: string])[] -) { - let result = string; - for (const [searchRegExp, replaceString] of replaceArgs) { - const beforeReplace = result; - result = result.replace( - canonicalizeMatch(searchRegExp), - canonicalizeReplace(replaceString, 'Vencord.Plugins.plugins["FakeProfileThemesAndEffects"]') - ); - if (beforeReplace === result) - throw new Error("Replace had no effect: " + searchRegExp); - } - return result; -} - -export const settings = definePluginSettings({ - prioritizeNitro: { - description: "Source to prioritize", - type: OptionType.SELECT, - options: [ - { label: "Nitro", value: true }, - { label: "About Me", value: false, default: true } - ] - }, - hideBuilder: { - description: "Hide the FPTE Builder in the User Profile and Server Profiles settings pages", - type: OptionType.BOOLEAN, - default: false - } -}); - -export default definePlugin({ - name: "FakeProfileThemesAndEffects", - description: "Allows profile theming and the usage of profile effects by hiding the colors and effect ID in your About Me using invisible, zero-width characters", - authors: [EquicordDevs.ryan], - patches: [ - // Patches UserProfileStore.getUserProfile - { - find: '"UserProfileStore"', - replacement: { - match: /([{}]getUserProfile\([^)]*\){return) ?([^}]+)/, - replace: "$1 $self.decodeAboutMeFPTEHook($2)" - } - }, - // Patches ProfileCustomizationPreview - { - find: ".EDIT_PROFILE_BANNER})", - replacement: { - match: /function \i\((\i)\){/, - replace: "$&$self.profilePreviewHook($1);" - } - }, - // Adds the FPTE Builder to the User Profile settings page - { - find: "DefaultCustomizationSections", - replacement: { - match: /\.sectionsContainer,.*?children:\[/, - replace: "$&$self.addFPTEBuilder()," - } - }, - // Adds the FPTE Builder to the Server Profiles settings page - { - find: '"guild should not be null"', - replacement: { - match: /\.sectionsContainer,.*?children:\[(?=.+?[{,]guild:(\i))/, - replace: "$&$self.addFPTEBuilder($1)," - } - }, - // ProfileEffectModal - { - find: '"ProfileEffectModal"', - group: true, - replacement: [ - // Modal root - { - match: /(function (\i)\([^)]*\){(?:.(?!function |}$))*className:\i\.modal,(?:.(?!function |}$))*}).*(?=})/, - replace: (match, func, funcName) => `${match}(()=>{$self.ProfileEffectModal=${funcName};` - + replaceHelper(func, [ - // Required for the profile preview to show profile effects - [ - /(?<=[{,]purchases:.+?}=).+?(?=,\i=|,{\i:|;)/, - "{isFetching:!1,categories:new Map,purchases:$self.usePurchases()}" - ] - ]) - + "})()" - }, - // Modal content - { - match: /(function \i\([^)]*\){(?:.(?!function ))*\.modalContent,(?:.(?!function ))*}).*(?=}\))/, - replace: (match, func) => match + replaceHelper(func, [ - // Required to show the apply button - [ - /(?<=[{,]purchase:.+?}=).+?(?=,\i=|,{\i:|;)/, - "{purchase:{purchasedAt:new Date}}" - ], - // Replaces the profile effect list with the modified version - [ - /(?<=\.jsxs?\)\()[^,]+(?=,{(?:(?:.(?!\.jsxs?\)))+,)?onSelect:)/, - "$self.ProfileEffectSelection" - ], - // Replaces the apply profile effect function with the modified version - [ - /(?<=[{,]onApply:).*?\)\((\i).*?(?=,\i:|}\))/, - "()=>$self.onApply($1)" - ], - // Required to show the apply button - [ - /(?<=[{,]canUseCollectibles:).+?(?=,\i:|}\))/, - "!0" - ], - // Required to enable the apply button - [ - /(?<=[{,]disableApplyButton:).+?(?=,\i:|}\))/, - "!1" - ] - ]) - } - ] - }, - // ProfileEffectSelection - { - find: ".presetEffectBackground", - replacement: { - match: /function\(\i,\i,.*?=>(\i).+[,;}]\1=([^=].+?})(?=;|}$).*(?=}$)/, - replace: (match, _, func) => `${match};$self.ProfileEffectSelection=` - + replaceHelper(func, [ - // Removes the "Exclusive to Nitro" and "Preview The Shop" sections - // Adds every profile effect to the "Your Decorations" section and removes the "Shop" button - [ - /(?<=[ ,](\i)=).+?(?=(?:,\i=|,{\i:|;).+?:\1\.map\()/, - "$self.useProfileEffectSections($&)" - ] - ]) - } - }, - // Patches ProfileEffectPreview - { - find: "#{intl::COLLECTIBLES_GIFT_LABEL}", - replacement: { - // Add back removed "forProfileEffectModal" property - match: /(?<=[{,])(?=pendingProfileEffect:)/, - replace: "forProfileEffectModal:!0," - } - } - ], - - start: () => { - const { FakeProfileThemesAndEffects, FakeProfileThemes } = Settings.plugins; - if (FakeProfileThemes.enabled && FakeProfileThemesAndEffects.enabled) { - FakeProfileThemes.enabled = false; - showToast("Disabled FakeProfileThemes as FakeProfileThemesAndEffects is enabled", Toasts.Type.SUCCESS); - } - }, - - addFPTEBuilder: (guild?: BuilderProps["guild"]) => settings.store.hideBuilder ? null : , - - onApply(_effectId?: string) { }, - set ProfileEffectModal(comp: Parameters[0]) { - setProfileEffectModal(props => { - this.onApply = effectId => { - props.onApply(effectId ? ProfileEffectStore.getProfileEffectById(effectId)!.config : null); - props.onClose(); - }; - return comp(props); - }); - }, - - ProfileEffectSelection: () => null, - - usePurchases: () => useMemo(() => { - return new Map( - ProfileEffectStore.getAllProfileEffects().map(effect => [ - effect.id, - { items: new ProfileEffectRecord(effect) } - ]) - ); - }, [ProfileEffectStore.getAllProfileEffects()]), - - useProfileEffectSections: (origSections: Record[]) => useMemo( - () => { - origSections.splice(1); - origSections[0].items.splice(1); - for (const effect of ProfileEffectStore.getAllProfileEffects()) - origSections[0].items.push(new ProfileEffectRecord(effect)); - return origSections; - }, - [ProfileEffectStore.getAllProfileEffects()] - ), - - settings, - settingsAboutComponent, - decodeAboutMeFPTEHook, - profilePreviewHook -}); diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/lib/fpte.ts b/src/equicordplugins/fakeProfileThemesAndEffects/lib/fpte.ts deleted file mode 100644 index 54412ff42..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/lib/fpte.ts +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -/** The FPTE delimiter codepoint (codepoint of zero-width space). */ -const DELIMITER_CODEPOINT = 0x200B; -/** The FPTE delimiter (zero-width space). */ -const DELIMITER = String.fromCodePoint(DELIMITER_CODEPOINT); -/** The FPTE radix (number of default-ignorable codepoints in the SSP plane). */ -const RADIX = 0x1000; -/** The FPTE starting codepoint (first codepoint in the SSP plane). */ -const STARTING_CODEPOINT = 0xE0000; -/** The FPTE starting character (first character in the SSP plane). */ -const STARTING = String.fromCodePoint(STARTING_CODEPOINT); -/** The FPTE ending codepoint (last default-ignorable codepoint in the SSP plane). */ -const ENDING_CODEPOINT = STARTING_CODEPOINT + RADIX - 1; - -/** - * Builds a theme color string in the legacy format: `[#primary,#accent]`, where primary and accent are - * 24-bit colors as base-16 strings, with each codepoint of the string offset by +{@link STARTING_CODEPOINT}. - * @param primary The 24-bit primary color. - * @param accent The 24-bit accent color. - * @returns The built legacy-format theme color string. - */ -export function encodeColorsLegacy(primary: number, accent: number) { - let str = ""; - for (const char of `[#${primary.toString(16)},#${accent.toString(16)}]`) - str += String.fromCodePoint(char.codePointAt(0)! + STARTING_CODEPOINT); - return str; -} - -/** - * Extracts the theme colors from a legacy-format string. - * @param str The legacy-format string to extract the theme colors from. - * @returns The profile theme colors. Colors will be -1 if not found. - * @see {@link encodeColorsLegacy} - */ -export function decodeColorsLegacy(str: string): [primaryColor: number, accentColor: number] { - const [primary, accent] = str.matchAll(/(?<=#)[\dA-Fa-f]{1,6}/g); - return [primary ? parseInt(primary[0], 16) : -1, accent ? parseInt(accent[0], 16) : -1]; -} - -/** - * Converts a 24-bit color to a base-{@link RADIX} string with each codepoint offset by +{@link STARTING_CODEPOINT}. - * @param color The 24-bit color to be converted. - * @returns The converted base-{@link RADIX} string with +{@link STARTING_CODEPOINT} offset. - */ -export function encodeColor(color: number) { - if (color === 0) return STARTING; - let str = ""; - for (; color > 0; color = Math.trunc(color / RADIX)) - str = String.fromCodePoint(color % RADIX + STARTING_CODEPOINT) + str; - return str; -} - -/** - * Converts a no-offset base-{@link RADIX} string to a 24-bit color. - * @param str The no-offset base-{@link RADIX} string to be converted. - * @returns The converted 24-bit color. - * Will be -1 if `str` is empty and -2 if the color is greater than the maximum 24-bit color, 0xFFFFFF. - */ -export function decodeColor(str: string) { - if (!str) return -1; - let color = 0; - for (let i = 0; i < str.length; i++) { - if (color > 0xFFF_FFF) return -2; - color += str.codePointAt(i)! * RADIX ** (str.length - 1 - i); - } - return color; -} - -/** - * Converts an effect ID to a base-{@link RADIX} string with each code point offset by +{@link STARTING_CODEPOINT}. - * @param id The effect ID to be converted. - * @returns The converted base-{@link RADIX} string with +{@link STARTING_CODEPOINT} offset. - */ -export function encodeEffect(id: bigint) { - if (id === 0n) return STARTING; - let str = ""; - for (; id > 0n; id /= BigInt(RADIX)) - str = String.fromCodePoint(Number(id % BigInt(RADIX)) + STARTING_CODEPOINT) + str; - return str; -} - -/** - * Converts a no-offset base-{@link RADIX} string to an effect ID. - * @param str The no-offset base-{@link RADIX} string to be converted. - * @returns The converted effect ID. - * Will be -1n if `str` is empty and -2n if the color is greater than the maximum effect ID. - */ -export function decodeEffect(str: string) { - if (!str) return -1n; - let id = 0n; - for (let i = 0; i < str.length; i++) { - if (id >= 10_000_000_000_000_000_000n) return -2n; - id += BigInt(str.codePointAt(i)!) * BigInt(RADIX) ** BigInt(str.length - 1 - i); - } - return id; -} - -/** - * Builds a FPTE string containing the given primary/accent colors and effect ID. If the FPTE Builder is NOT set to backwards - * compatibility mode, the primary and accent colors will be converted to base-{@link RADIX} before they are encoded. - * @param primary The primary profile theme color. Must be negative if unset. - * @param accent The accent profile theme color. Must be negative if unset. - * @param effect The profile effect ID. Must be empty if unset. - * @param legacy Whether the primary and accent colors should be legacy encoded. - * @returns The built FPTE string. Will be empty if the given colors and effect are all unset. - */ -export function buildFPTE(primary: number, accent: number, effect: string, legacy: boolean) { - /** The FPTE string to be returned. */ - let fpte = ""; - - // If the FPTE Builder is set to backwards compatibility mode, - // the primary and accent colors, if set, will be legacy encoded. - if (legacy) { - // Legacy FPTE strings must include both the primary and accent colors, even if they are the same. - - if (primary >= 0) { - // If both the primary and accent colors are set, they will be legacy encoded and added to the - // string; otherwise, if the accent color is unset, the primary color will be used in its place. - if (accent >= 0) - fpte = encodeColorsLegacy(primary, accent); - else - fpte = encodeColorsLegacy(primary, primary); - - // If the effect ID is set, it will be encoded and added to the string prefixed by one delimiter. - if (effect) - fpte += DELIMITER + encodeEffect(BigInt(effect)); - - return fpte; - } - - // Since the primary color is unset, the accent color, if set, will be used in its place. - if (accent >= 0) { - fpte = encodeColorsLegacy(accent, accent); - - // If the effect ID is set, it will be encoded and added to the string prefixed by one delimiter. - if (effect) - fpte += DELIMITER + encodeEffect(BigInt(effect)); - - return fpte; - } - } - // If the primary color is set, it will be encoded and added to the string. - else if (primary >= 0) { - fpte = encodeColor(primary); - - // If the accent color is set and different from the primary color, it - // will be encoded and added to the string prefixed by one delimiter. - if (accent >= 0 && primary !== accent) { - fpte += DELIMITER + encodeColor(accent); - - // If the effect ID is set, it will be encoded and added to the string prefixed by one delimiter. - if (effect) - fpte += DELIMITER + encodeEffect(BigInt(effect)); - - return fpte; - } - } - // If only the accent color is set, it will be encoded and added to the string. - else if (accent >= 0) - fpte = encodeColor(accent); - - // Since either the primary/accent colors are the same, both are unset, or just one is set, only one color will be added - // to the string; therefore, the effect ID, if set, will be encoded and added to the string prefixed by two delimiters. - if (effect) - fpte += DELIMITER + DELIMITER + encodeEffect(BigInt(effect)); - - return fpte; -} - -/** - * Extracts the delimiter-separated values of the first FPTE substring in a string. - * @param str The string to be searched for a FPTE substring. - * @returns An array of the found FPTE substring's extracted values. Values will be empty if not found. - */ -export function extractFPTE(str: string) { - /** The array of extracted FPTE values to be returned. */ - const fpte: [maybePrimaryOrLegacy: string, maybeAccentOrEffect: string, maybeEffect: string] = ["", "", ""]; - /** The current index of {@link fpte} getting extracted. */ - let i = 0; - - for (const char of str) { - /** The current character's codepoint. */ - const cp = char.codePointAt(0)!; - - // If the current character is a delimiter, then the current index of fpte has been completed. - if (cp === DELIMITER_CODEPOINT) { - // If the current index of fpte is the last, then the extraction is done. - if (i >= 2) break; - i++; // Start extracting the next index of fpte. - } - // If the current character is not a delimiter but a valid FPTE - // character, it will be added to the current index of fpte. - else if (cp >= STARTING_CODEPOINT && cp <= ENDING_CODEPOINT) - fpte[i] += String.fromCodePoint(cp - STARTING_CODEPOINT); - // If an FPTE string has been found and its end has been reached, then the extraction is done. - else if (i > 0 || fpte[0]) break; - } - - return fpte; -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/lib/index.ts b/src/equicordplugins/fakeProfileThemesAndEffects/lib/index.ts deleted file mode 100644 index afc0c10e8..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/lib/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export * from "./fpte"; -export * from "./profileEffects"; -export * from "./profilePreview"; -export * from "./userProfile"; diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/lib/profileEffects.ts b/src/equicordplugins/fakeProfileThemesAndEffects/lib/profileEffects.ts deleted file mode 100644 index 04f93cc8c..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/lib/profileEffects.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import type { FluxStore } from "@vencord/discord-types"; -import { findByCodeLazy, findStoreLazy } from "@webpack"; -import type { SnakeCasedProperties } from "type-fest"; - -export const ProfileEffectStore: FluxStore & { - canFetchAll: () => boolean; - getProfileEffectById: (effectId: string) => ProfileEffect | undefined; - isFetchingAll: () => boolean; - getAllProfileEffects: () => ProfileEffect[]; -} = findStoreLazy("ProfileEffectStore"); - -export const ProfileEffectRecord: { - new(profileEffectProperties: ProfileEffectProperties): ProfileEffectRecordInstance; - fromServer: (profileEffectFromServer: SnakeCasedProperties) => ProfileEffectRecordInstance; -} = findByCodeLazy(",this.type=", ".PROFILE_EFFECT"); - -export type ProfileEffectProperties = Omit; - -export interface ProfileEffectRecordInstance { - id: string; - skuId: string; - type: CollectiblesItemType.PROFILE_EFFECT; -} - -export interface ProfileEffect { - config: ProfileEffectConfig; - id: string; - skuId: string; -} - -export interface ProfileEffectConfig { - accessibilityLabel: string; - animationType: number; - description: string; - effects: { - duration: number; - height: number; - loop: boolean; - loopDelay: number; - position: { - x: number; - y: number; - }; - src: string; - start: number; - width: number; - zIndex: number; - }[]; - id: string; - reducedMotionSrc: string; - skuId: string; - staticFrameSrc?: string; - thumbnailPreviewSrc: string; - title: string; - type: CollectiblesItemType.PROFILE_EFFECT; -} - -export const enum CollectiblesItemType { - AVATAR_DECORATION = 0, - PROFILE_EFFECT = 1, - NONE = 100, - BUNDLE = 1_000, -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/lib/profilePreview.ts b/src/equicordplugins/fakeProfileThemesAndEffects/lib/profilePreview.ts deleted file mode 100644 index 0b8bc8904..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/lib/profilePreview.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { FluxDispatcher, useState } from "@webpack/common"; - -import type { ProfileEffectConfig } from "./profileEffects"; - -function updatePreview() { - FluxDispatcher.dispatch({ type: "USER_SETTINGS_ACCOUNT_SUBMIT_SUCCESS" }); -} - -let primaryColor: number | null = null; -export function usePrimaryColor(initialState: typeof primaryColor) { - const [state, setState] = useState(() => primaryColor = initialState); - return [ - state, - (color: typeof primaryColor) => { - setState(primaryColor = color); - if (showPreview) updatePreview(); - } - ] as const; -} - -let accentColor: number | null = null; -export function useAccentColor(initialState: typeof accentColor) { - const [state, setState] = useState(() => accentColor = initialState); - return [ - state, - (color: typeof accentColor) => { - setState(accentColor = color); - if (showPreview) updatePreview(); - } - ] as const; -} - -let profileEffect: ProfileEffectConfig | null = null; -export function useProfileEffect(initialState: typeof profileEffect) { - const [state, setState] = useState(() => profileEffect = initialState); - return [ - state, - (effect: typeof profileEffect) => { - setState(profileEffect = effect); - if (showPreview) updatePreview(); - } - ] as const; -} - -let showPreview = true; -export function useShowPreview(initialState: typeof showPreview) { - const [state, setState] = useState(() => showPreview = initialState); - return [ - state, - (preview: typeof showPreview) => { - setState(showPreview = preview); - updatePreview(); - } - ] as const; -} - -export function profilePreviewHook(props: Record) { - if (showPreview) { - if (primaryColor !== null) { - props.pendingThemeColors = [primaryColor, accentColor ?? primaryColor]; - props.canUsePremiumCustomization = true; - } else if (accentColor !== null) { - props.pendingThemeColors = [accentColor, accentColor]; - props.canUsePremiumCustomization = true; - } - if (!props.forProfileEffectModal && profileEffect) { - props.pendingProfileEffect = profileEffect; - props.canUsePremiumCustomization = true; - } - } -} diff --git a/src/equicordplugins/fakeProfileThemesAndEffects/lib/userProfile.ts b/src/equicordplugins/fakeProfileThemesAndEffects/lib/userProfile.ts deleted file mode 100644 index 3c90fb328..000000000 --- a/src/equicordplugins/fakeProfileThemesAndEffects/lib/userProfile.ts +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { settings } from ".."; -import { decodeColor, decodeColorsLegacy, decodeEffect, extractFPTE } from "./fpte"; -import { ProfileEffectStore } from "./profileEffects"; - -export interface UserProfile { - bio: string; - premiumType: number | null | undefined; - profileEffectId: string | undefined; - profileEffect: any; - themeColors: [primaryColor: number, accentColor: number] | undefined; -} - -function updateProfileThemeColors(profile: UserProfile, primary: number, accent: number) { - if (primary > -1) { - profile.themeColors = [primary, accent > -1 ? accent : primary]; - profile.premiumType = 2; - } else if (accent > -1) { - profile.themeColors = [accent, accent]; - profile.premiumType = 2; - } -} - -function updateProfileEffectId(profile: UserProfile, id: bigint) { - if (id > -1n) { - profile.profileEffect = ProfileEffectStore.getProfileEffectById(id.toString()); - profile.profileEffectId = id.toString(); - profile.premiumType = 2; - } -} - -export function decodeAboutMeFPTEHook(profile?: UserProfile) { - if (!profile) return profile; - - if (settings.store.prioritizeNitro) { - if (profile.themeColors) { - if (!profile.profileEffectId) { - const fpte = extractFPTE(profile.bio); - if (decodeColor(fpte[0]) === -2) - updateProfileEffectId(profile, decodeEffect(fpte[1])); - else - updateProfileEffectId(profile, decodeEffect(fpte[2])); - } - return profile; - } else if (profile.profileEffectId) { - const fpte = extractFPTE(profile.bio); - const primaryColor = decodeColor(fpte[0]); - if (primaryColor === -2) - updateProfileThemeColors(profile, ...decodeColorsLegacy(fpte[0])); - else - updateProfileThemeColors(profile, primaryColor, decodeColor(fpte[1])); - return profile; - } - } - - const fpte = extractFPTE(profile.bio); - const primaryColor = decodeColor(fpte[0]); - if (primaryColor === -2) { - updateProfileThemeColors(profile, ...decodeColorsLegacy(fpte[0])); - updateProfileEffectId(profile, decodeEffect(fpte[1])); - } else { - updateProfileThemeColors(profile, primaryColor, decodeColor(fpte[1])); - updateProfileEffectId(profile, decodeEffect(fpte[2])); - } - - return profile; -}