mirror of
https://github.com/Vendicated/Vencord
synced 2024-09-13 04:29:24 -04:00
fix: make compatible with post-doomsday Discord
This commit is contained in:
parent
ffebe32af5
commit
780b865e79
6 changed files with 128 additions and 154 deletions
|
@ -6,23 +6,24 @@
|
|||
|
||||
import { copyWithToast } from "@utils/misc";
|
||||
import { Button, showToast, Switch, UserStore, useState } from "@webpack/common";
|
||||
import type { Guild } from "discord-types/general";
|
||||
|
||||
import { buildFPTE } from "../lib/fpte";
|
||||
import { useAccentColor, usePrimaryColor, useProfileEffect, useShowPreview } from "../lib/profilePreview";
|
||||
import { BuilderButton, BuilderColorButton, CustomizationSection, openProfileEffectModal, useAvatarColors } from ".";
|
||||
|
||||
export interface BuilderProps {
|
||||
guildId?: string | undefined;
|
||||
guild?: Guild | undefined;
|
||||
}
|
||||
|
||||
export function Builder({ guildId }: BuilderProps) {
|
||||
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(guildId, 80));
|
||||
const avatarColors = useAvatarColors(UserStore.getCurrentUser().getAvatarURL(guild?.id, 80));
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -48,7 +49,7 @@ export function Builder({ guildId }: BuilderProps) {
|
|||
} : undefined}
|
||||
buttonProps={{
|
||||
onClick() {
|
||||
openProfileEffectModal(effect?.id, setEffect);
|
||||
openProfileEffectModal(effect?.id, setEffect, guild);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -26,7 +26,7 @@ export const BuilderColorButton = ({ label, color, setColor, suggestedColors }:
|
|||
)}
|
||||
>
|
||||
{popoutProps => {
|
||||
const hexColor = color?.toString(16).padStart(6, "0").padStart(7, "#");
|
||||
const hexColor = color ? "#" + color.toString(16).padStart(6, "0") : undefined;
|
||||
|
||||
return (
|
||||
<BuilderButton
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
*/
|
||||
|
||||
import { type ModalProps, openModal } from "@utils/modal";
|
||||
import { extractAndLoadChunksLazy } from "@webpack";
|
||||
import { extractAndLoadChunksLazy, findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import type { Guild } from "discord-types/general";
|
||||
import type { ComponentType, FunctionComponent, PropsWithChildren, ReactNode } from "react";
|
||||
|
||||
import type { ProfileEffectConfig } from "../lib/profileEffects";
|
||||
|
@ -15,51 +16,49 @@ export * from "./BuilderButton";
|
|||
export * from "./BuilderColorButton";
|
||||
export * from "./settingsAboutComponent";
|
||||
|
||||
export interface CustomColorPickerProps {
|
||||
value?: number | null | undefined;
|
||||
onChange: (color: number) => void;
|
||||
onClose?: (() => void) | undefined;
|
||||
suggestedColors?: string[] | undefined;
|
||||
middle?: ReactNode;
|
||||
footer?: ReactNode;
|
||||
showEyeDropper?: boolean | undefined;
|
||||
}
|
||||
|
||||
export let CustomColorPicker: ComponentType<CustomColorPickerProps> = () => null;
|
||||
|
||||
export function setCustomColorPicker(comp: typeof CustomColorPicker) {
|
||||
CustomColorPicker = comp;
|
||||
}
|
||||
|
||||
export let useAvatarColors: (avatarURL: string, fillerColor?: string | undefined, desaturateColors?: boolean | undefined) => string[] = () => [];
|
||||
|
||||
export function setUseAvatarColors(hook: typeof useAvatarColors) {
|
||||
useAvatarColors = hook;
|
||||
}
|
||||
|
||||
export interface CustomizationSectionProps {
|
||||
export interface CustomizationSectionProps extends PropsWithChildren {
|
||||
borderType?: "limited" | "premium" | 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;
|
||||
description?: ReactNode;
|
||||
}
|
||||
|
||||
export const CustomizationSection: ComponentType<CustomizationSectionProps>
|
||||
= findByCodeLazy(".customizationSectionBackground");
|
||||
|
||||
export const useAvatarColors: (
|
||||
avatarURL: string,
|
||||
fillerColor?: string | undefined,
|
||||
desaturateColors?: boolean | undefined /* = true */
|
||||
) => string[] = findByCodeLazy(".palette[", ".desaturateUserColors");
|
||||
|
||||
export interface CustomColorPickerProps {
|
||||
className?: string | undefined;
|
||||
errors?: string[] | undefined;
|
||||
disabled?: boolean | undefined;
|
||||
hideDivider?: boolean | undefined;
|
||||
showBorder?: boolean | undefined;
|
||||
borderType?: "limited" | "premium" | undefined;
|
||||
hasBackground?: boolean | undefined;
|
||||
forcedDivider?: boolean | undefined;
|
||||
showPremiumIcon?: boolean | undefined;
|
||||
eagerUpdate?: boolean | undefined /* = false */;
|
||||
footer?: ReactNode;
|
||||
middle?: ReactNode;
|
||||
onChange: (color: number) => void;
|
||||
onClose?: (() => void) | undefined;
|
||||
showEyeDropper?: boolean | null | undefined /* = false */;
|
||||
suggestedColors?: string[] | undefined;
|
||||
wrapperComponentType?: ComponentType | null | undefined;
|
||||
value?: string | number | null | undefined;
|
||||
}
|
||||
|
||||
export let CustomizationSection: ComponentType<PropsWithChildren<CustomizationSectionProps>> = () => null;
|
||||
|
||||
export function setCustomizationSection(comp: typeof CustomizationSection) {
|
||||
CustomizationSection = comp;
|
||||
}
|
||||
export const CustomColorPicker = findComponentByCodeLazy<CustomColorPickerProps>(".customColorPicker");
|
||||
|
||||
export interface ProfileEffectModalProps extends ModalProps {
|
||||
analyticsLocations?: string[] | undefined;
|
||||
guild?: Guild | null | undefined;
|
||||
initialSelectedEffectId?: string | undefined;
|
||||
onApply: (effect: ProfileEffectConfig | null) => void;
|
||||
}
|
||||
|
@ -70,16 +69,20 @@ export function setProfileEffectModal(comp: typeof ProfileEffectModal) {
|
|||
ProfileEffectModal = comp;
|
||||
}
|
||||
|
||||
const requireProfileEffectModal = extractAndLoadChunksLazy(["openProfileEffectModal:function(){"]);
|
||||
const requireProfileEffectModal = extractAndLoadChunksLazy([".openModalLazy", "initialSelectedEffectId:"]);
|
||||
|
||||
export function openProfileEffectModal(initialEffectId: ProfileEffectModalProps["initialSelectedEffectId"], onApply: ProfileEffectModalProps["onApply"]) {
|
||||
requireProfileEffectModal().then(() => {
|
||||
openModal(modalProps => (
|
||||
<ProfileEffectModal
|
||||
{...modalProps}
|
||||
initialSelectedEffectId={initialEffectId}
|
||||
onApply={onApply}
|
||||
/>
|
||||
));
|
||||
});
|
||||
export async function openProfileEffectModal(
|
||||
initialEffectId: ProfileEffectModalProps["initialSelectedEffectId"],
|
||||
onApply: ProfileEffectModalProps["onApply"],
|
||||
guild?: ProfileEffectModalProps["guild"]
|
||||
) {
|
||||
await requireProfileEffectModal();
|
||||
openModal(modalProps => (
|
||||
<ProfileEffectModal
|
||||
{...modalProps}
|
||||
initialSelectedEffectId={initialEffectId}
|
||||
guild={guild}
|
||||
onApply={onApply}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
|
|||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { useMemo } from "@webpack/common";
|
||||
|
||||
import { Builder, type BuilderProps, setCustomColorPicker, setCustomizationSection, setProfileEffectModal, settingsAboutComponent, setUseAvatarColors } from "./components";
|
||||
import { ProfileEffectRecord, ProfileEffectStore, setProfileEffectRecord, setProfileEffectStore } from "./lib/profileEffects";
|
||||
import { Builder, type BuilderProps, setProfileEffectModal, settingsAboutComponent } from "./components";
|
||||
import { ProfileEffectRecord, ProfileEffectStore } from "./lib/profileEffects";
|
||||
import { profilePreviewHook } from "./lib/profilePreview";
|
||||
import { decodeAboutMeFPTEHook } from "./lib/userProfile";
|
||||
|
||||
|
@ -21,7 +21,7 @@ function replaceHelper(string: string, replaceArgs: [searchRegExp: RegExp, repla
|
|||
const beforeReplace = result;
|
||||
result = result.replace(
|
||||
canonicalizeMatch(searchRegExp),
|
||||
canonicalizeReplace(replaceString, "FakeProfileThemesAndEffects") as string
|
||||
canonicalizeReplace(replaceString, "FakeProfileThemesAndEffects")
|
||||
);
|
||||
if (beforeReplace === result)
|
||||
throw new Error("Replace had no effect: " + searchRegExp);
|
||||
|
@ -76,67 +76,33 @@ export default definePlugin({
|
|||
},
|
||||
// Adds the FPTE Builder to the Server Profiles settings page
|
||||
{
|
||||
find: ".setNewPendingGuildIdentity",
|
||||
find: '"guild should not be null"',
|
||||
replacement: {
|
||||
match: /\.sectionsContainer,.*?children:\[(?=.+?[{,]guild:(\i))/,
|
||||
replace: "$&$self.addFPTEBuilder($1),"
|
||||
}
|
||||
},
|
||||
// CustomizationSection
|
||||
{
|
||||
find: ".customizationSectionBackground",
|
||||
replacement: {
|
||||
match: /default:function\(\){return (\i)}.+?;/,
|
||||
replace: "$&$self.CustomizationSection=$1;"
|
||||
}
|
||||
},
|
||||
// CustomColorPicker
|
||||
{
|
||||
find: ".colorPickerSwatch",
|
||||
replacement: {
|
||||
match: /CustomColorPicker:function\(\){return (\i)}.+?[ ,;}]\1=(?!=)/,
|
||||
replace: "$&$self.CustomColorPicker="
|
||||
}
|
||||
},
|
||||
// useAvatarColors
|
||||
{
|
||||
find: "useAvatarColors:function(){",
|
||||
replacement: {
|
||||
match: /useAvatarColors:function\(\){return (\i)}.+?;/,
|
||||
replace: "$&$self.useAvatarColors=$1;"
|
||||
}
|
||||
},
|
||||
// ProfileEffectRecord
|
||||
{
|
||||
find: "isProfileEffectRecord:function(){",
|
||||
replacement: {
|
||||
match: /default:function\(\){return (\i)}.+(?=}$)/,
|
||||
replace: "$&;$self.ProfileEffectRecord=$1"
|
||||
}
|
||||
},
|
||||
// ProfileEffectStore
|
||||
{
|
||||
find: '"ProfileEffectStore"',
|
||||
replacement: {
|
||||
match: /function\(\i,(\i),.+[,;}]\1\.default=(?!=)/,
|
||||
replace: "$&$self.ProfileEffectStore="
|
||||
}
|
||||
},
|
||||
// ProfileEffectModal
|
||||
{
|
||||
find: "initialSelectedProfileEffectId",
|
||||
replacement: {
|
||||
match: /default:function\(\){return (\i)}(?=.+?(function \1\((?:.(?!function |}$))+\.jsxs?\)\((\i),.+?})(?:function |}$)).+?(function \3\(.+?})(?=function |}$).*(?=}$)/,
|
||||
replace: (wpModule, modalRootName, ModalRoot, _modalInnerName, ModalInner) => (
|
||||
`${wpModule}{$self.ProfileEffectModal=${modalRootName};`
|
||||
+ replaceHelper(ModalRoot, [
|
||||
// Required for the profile preview to show profile effects
|
||||
[
|
||||
/(?<=[{,]purchases:.+?}=).+?(?=,\i=|,{\i:|;)/,
|
||||
"{isFetching:!1,categories:new Map,purchases:$self.getPurchases()}"
|
||||
]
|
||||
])
|
||||
+ replaceHelper(ModalInner, [
|
||||
replacement: [
|
||||
// Modal root
|
||||
{
|
||||
match: /(function (\i)\([^)]*\){(?:.(?!function |}$))*\.ModalRoot,(?:.(?!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.getPurchases()}"
|
||||
]
|
||||
])
|
||||
+ "}"
|
||||
},
|
||||
// Modal content
|
||||
{
|
||||
match: /(function \i\([^)]*\){(?:.(?!function ))*\.ModalContent,(?:.(?!function ))*}).*(?=}}$)/,
|
||||
replace: (match, func) => match + replaceHelper(func, [
|
||||
// Required to show the apply button
|
||||
[
|
||||
/(?<=[{,]purchase:.+?}=).+?(?=,\i=|,{\i:|;)/,
|
||||
|
@ -149,7 +115,7 @@ export default definePlugin({
|
|||
],
|
||||
// Replaces the apply profile effect function with the modified version
|
||||
[
|
||||
/(?<=[{,]onApply:).+?\.setNewPendingProfileEffectId\)\((\i).+?(?=,\i:|}\))/,
|
||||
/(?<=[{,]onApply:).*?\)\((\i).*?(?=,\i:|}\))/,
|
||||
"()=>$self.onApply($1)"
|
||||
],
|
||||
// Required to show the apply button
|
||||
|
@ -163,18 +129,17 @@ export default definePlugin({
|
|||
"!1"
|
||||
]
|
||||
])
|
||||
+ "}"
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
group: true
|
||||
},
|
||||
// ProfileEffectModalList
|
||||
{
|
||||
find: "selectedProfileEffectRef",
|
||||
replacement: {
|
||||
match: /function\(\i,(\i),.+[,;}]\1\.default=([^=].+?})(?=;|}$).*(?=}$)/,
|
||||
replace: (wpModule, _wpModuleVar, List) => (
|
||||
`${wpModule};$self.ProfileEffectModalList=`
|
||||
+ replaceHelper(List, [
|
||||
match: /function\(\i,(\i),.+[,;}]\1\.\i=([^=].+?})(?=;|}$).*(?=}$)/,
|
||||
replace: (match, _, func) => `${match};$self.ProfileEffectModalList=`
|
||||
+ 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
|
||||
[
|
||||
|
@ -182,14 +147,13 @@ export default definePlugin({
|
|||
"$self.getListSections($&)"
|
||||
]
|
||||
])
|
||||
)
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
addFPTEBuilder: (guildId?: BuilderProps["guildId"]) => settings.store.hideBuilder ? null : <Builder guildId={guildId} />,
|
||||
addFPTEBuilder: (guild?: BuilderProps["guild"]) => settings.store.hideBuilder ? null : <Builder guild={guild} />,
|
||||
|
||||
onApply(_effectId: string | undefined) { },
|
||||
onApply(_effectId: string | undefined) {},
|
||||
set ProfileEffectModal(comp: Parameters<typeof setProfileEffectModal>[0]) {
|
||||
setProfileEffectModal(props => {
|
||||
this.onApply = effectId => {
|
||||
|
@ -210,7 +174,7 @@ export default definePlugin({
|
|||
[ProfileEffectStore.profileEffects]
|
||||
),
|
||||
|
||||
getListSections: (origSections: any[]) => useMemo(
|
||||
getListSections: (origSections: Record<string, any>[]) => useMemo(
|
||||
() => {
|
||||
origSections.splice(1);
|
||||
origSections[0].items.splice(1);
|
||||
|
@ -222,12 +186,6 @@ export default definePlugin({
|
|||
[ProfileEffectStore.profileEffects]
|
||||
),
|
||||
|
||||
set CustomizationSection(comp: Parameters<typeof setCustomizationSection>[0]) { setCustomizationSection(comp); },
|
||||
set CustomColorPicker(comp: Parameters<typeof setCustomColorPicker>[0]) { setCustomColorPicker(comp); },
|
||||
set useAvatarColors(hook: Parameters<typeof setUseAvatarColors>[0]) { setUseAvatarColors(hook); },
|
||||
set ProfileEffectRecord(obj: Parameters<typeof setProfileEffectRecord>[0]) { setProfileEffectRecord(obj); },
|
||||
set ProfileEffectStore(store: Parameters<typeof setProfileEffectStore>[0]) { setProfileEffectStore(store); },
|
||||
|
||||
settingsAboutComponent,
|
||||
settings,
|
||||
decodeAboutMeFPTEHook,
|
||||
|
|
|
@ -4,7 +4,38 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { findByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import type { FluxStore } from "@webpack/types";
|
||||
import type { SnakeCasedProperties } from "type-fest";
|
||||
|
||||
export const ProfileEffectStore: FluxStore & {
|
||||
canFetch: () => boolean;
|
||||
getProfileEffectById: (effectId: string) => ProfileEffect | undefined;
|
||||
hasFetched: () => boolean;
|
||||
readonly fetchError: Error | undefined;
|
||||
readonly isFetching: boolean;
|
||||
readonly profileEffects: ProfileEffect[];
|
||||
readonly tryItOutId: string | null;
|
||||
} = findStoreLazy("ProfileEffectStore");
|
||||
|
||||
export const ProfileEffectRecord: {
|
||||
new (profileEffectProperties: ProfileEffectProperties): ProfileEffectRecordInstance;
|
||||
fromServer: (profileEffectFromServer: SnakeCasedProperties<ProfileEffectProperties>) => ProfileEffectRecordInstance;
|
||||
} = findByCodeLazy(",this.type=", ".PROFILE_EFFECT");
|
||||
|
||||
type ProfileEffectProperties = Omit<ProfileEffectRecordInstance, "type">;
|
||||
|
||||
interface ProfileEffectRecordInstance {
|
||||
id: string;
|
||||
skuId: string;
|
||||
type: CollectiblesItemType.PROFILE_EFFECT;
|
||||
}
|
||||
|
||||
export interface ProfileEffect {
|
||||
config: ProfileEffectConfig;
|
||||
id: string;
|
||||
skuId: string;
|
||||
}
|
||||
|
||||
export interface ProfileEffectConfig {
|
||||
accessibilityLabel: string;
|
||||
|
@ -30,31 +61,12 @@ export interface ProfileEffectConfig {
|
|||
staticFrameSrc?: string;
|
||||
thumbnailPreviewSrc: string;
|
||||
title: string;
|
||||
type: 1;
|
||||
type: CollectiblesItemType.PROFILE_EFFECT;
|
||||
}
|
||||
|
||||
export interface ProfileEffect extends Pick<ProfileEffectConfig, "id"> {
|
||||
config: ProfileEffectConfig;
|
||||
skuId: ProfileEffectConfig["sku_id"];
|
||||
}
|
||||
|
||||
export let ProfileEffectRecord: {
|
||||
new (effect: Omit<ProfileEffect, "config">): typeof effect & Pick<ProfileEffectConfig, "type">;
|
||||
fromServer: (effect: Pick<ProfileEffectConfig, "id" | "sku_id">) => Omit<ProfileEffect, "config"> & Pick<ProfileEffectConfig, "type">;
|
||||
};
|
||||
|
||||
export function setProfileEffectRecord(obj: typeof ProfileEffectRecord) {
|
||||
ProfileEffectRecord = obj;
|
||||
}
|
||||
|
||||
export let ProfileEffectStore: FluxStore & {
|
||||
readonly isFetching: boolean;
|
||||
readonly fetchError: Error | undefined;
|
||||
readonly profileEffects: ProfileEffect[];
|
||||
readonly tryItOutId: string | null;
|
||||
getProfileEffectById: (effectId: string) => ProfileEffect | undefined;
|
||||
};
|
||||
|
||||
export function setProfileEffectStore(store: typeof ProfileEffectStore) {
|
||||
ProfileEffectStore = store;
|
||||
export const enum CollectiblesItemType {
|
||||
AVATAR_DECORATION = 0,
|
||||
PROFILE_EFFECT = 1,
|
||||
NONE = 100,
|
||||
BUNDLE = 1_000,
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ export function useShowPreview(initialState: typeof showPreview) {
|
|||
] as const;
|
||||
}
|
||||
|
||||
export function profilePreviewHook(props: any) {
|
||||
export function profilePreviewHook(props: Record<string, any>) {
|
||||
if (showPreview) {
|
||||
if (primaryColor !== null) {
|
||||
props.pendingThemeColors = [primaryColor, accentColor ?? primaryColor];
|
||||
|
|
Loading…
Reference in a new issue