diff --git a/src/equicordplugins/discordColorways/colorwaysAPI.ts b/src/equicordplugins/discordColorways/colorwaysAPI.ts new file mode 100644 index 00000000..67fdff32 --- /dev/null +++ b/src/equicordplugins/discordColorways/colorwaysAPI.ts @@ -0,0 +1,18 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export const ColorwayCSS = { + get: () => document.getElementById("activeColorwayCSS")!.textContent || "", + set: (e: string) => { + if (!document.getElementById("activeColorwayCSS")) { + document.head.append(Object.assign(document.createElement("style"), { + id: "activeColorwayCSS", + textContent: e + })); + } else (document.getElementById("activeColorwayCSS") as any).textContent = e; + }, + remove: () => document.getElementById("activeColorwayCSS")?.remove(), +}; diff --git a/src/equicordplugins/discordColorways/components/AutoColorwaySelector.tsx b/src/equicordplugins/discordColorways/components/AutoColorwaySelector.tsx index 6ebbdace..e949a488 100644 --- a/src/equicordplugins/discordColorways/components/AutoColorwaySelector.tsx +++ b/src/equicordplugins/discordColorways/components/AutoColorwaySelector.tsx @@ -4,52 +4,49 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { DataStore } from "@api/index"; -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { Button, Forms, Text, useState } from "@webpack/common"; - +import { DataStore, useEffect, useState } from "../"; import { getAutoPresets } from "../css"; +import { ModalProps } from "../types"; export default function ({ modalProps, onChange, autoColorwayId = "" }: { modalProps: ModalProps, onChange: (autoPresetId: string) => void, autoColorwayId: string; }) { const [autoId, setAutoId] = useState(autoColorwayId); - const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar"); - return - - - Auto Preset Settings - - - + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + return
+

+ Auto Preset Settings +

+
About the Auto Colorway The auto colorway allows you to use your system's accent color in combination with a selection of presets that will fully utilize it.
- Presets: - {Object.values(getAutoPresets()).map(autoPreset => { - return
-
{ - setAutoId(autoPreset.id); - }}> - - {autoPreset.name} -
-
; - })} + Presets: + {Object.values(getAutoPresets()).map(autoPreset =>
{ + setAutoId(autoPreset.id); + }}> + + {autoPreset.name} +
)}
- - - - - - ; + +
+
; } diff --git a/src/equicordplugins/discordColorways/components/ColorPicker.tsx b/src/equicordplugins/discordColorways/components/ColorPicker.tsx index d4ae6adc..0c020e58 100644 --- a/src/equicordplugins/discordColorways/components/ColorPicker.tsx +++ b/src/equicordplugins/discordColorways/components/ColorPicker.tsx @@ -4,28 +4,25 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { Flex } from "@components/Flex"; -import { CopyIcon } from "@components/Icons"; -import { - ModalProps, - ModalRoot, -} from "@utils/modal"; -import { - Button, - Clipboard, - ScrollerThin, - TextInput, - Toasts, - useState, -} from "@webpack/common"; - +import { DataStore, Toasts, useEffect, useState } from ".."; import { mainColors } from "../constants"; import { colorVariables } from "../css"; +import { ModalProps } from "../types"; import { getHex } from "../utils"; +import { CopyIcon } from "./Icons"; export default function ({ modalProps }: { modalProps: ModalProps; }) { const [ColorVars, setColorVars] = useState(colorVariables); const [collapsedSettings, setCollapsedSettings] = useState(true); + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + let results: string[]; function searchToolboxItems(e: string) { results = []; @@ -37,55 +34,53 @@ export default function ({ modalProps }: { modalProps: ModalProps; }) { setColorVars(results); } - return - - +
+ { - searchToolboxItems(e); - if (e) { + onChange={({ currentTarget: { value } }) => { + searchToolboxItems(value); + if (value) { setCollapsedSettings(false); } else { setCollapsedSettings(true); } }} /> - - - + +
+
{ColorVars.map((colorVariable: string) =>
{ - Clipboard.copy(getHex(getComputedStyle(document.body).getPropertyValue("--" + colorVariable))); + navigator.clipboard.writeText(getHex(getComputedStyle(document.body).getPropertyValue("--" + colorVariable))); Toasts.show({ message: "Color " + colorVariable + " copied to clipboard", id: "toolbox-color-var-copied", type: 1 }); }} style={{ "--brand-experiment": `var(--${colorVariable})` } as React.CSSProperties}> {`Copy ${colorVariable}`}
)} - - +
+
{mainColors.map(mainColor =>
{ - Clipboard.copy(getHex(getComputedStyle(document.body).getPropertyValue(mainColor.var))); + navigator.clipboard.writeText(getHex(getComputedStyle(document.body).getPropertyValue(mainColor.var))); Toasts.show({ message: `${mainColor.title} color copied to clipboard`, id: `toolbox-${mainColor.name}-color-copied`, type: 1 }); }} width={20} height={20} className="colorwayToolbox-listItemSVG" /> {`Copy ${mainColor.title} Color`}
)} - - ; +
+ ; } diff --git a/src/equicordplugins/discordColorways/components/ColorwayCreatorSettingsModal.tsx b/src/equicordplugins/discordColorways/components/ColorwayCreatorSettingsModal.tsx index c969b01c..4be57145 100644 --- a/src/equicordplugins/discordColorways/components/ColorwayCreatorSettingsModal.tsx +++ b/src/equicordplugins/discordColorways/components/ColorwayCreatorSettingsModal.tsx @@ -4,67 +4,70 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { Button, Forms, ScrollerThin, Switch, Text, useState } from "@webpack/common"; - +import { DataStore, useEffect, useState } from ".."; import { getPreset } from "../css"; +import { ModalProps } from "../types"; +import Setting from "./Setting"; +import Switch from "./Switch"; export default function ({ modalProps, onSettings, presetId, hasTintedText, hasDiscordSaturation }: { modalProps: ModalProps, presetId: string, hasTintedText: boolean, hasDiscordSaturation: boolean, onSettings: ({ presetId, tintedText, discordSaturation }: { presetId: string, tintedText: boolean, discordSaturation: boolean; }) => void; }) { const [tintedText, setTintedText] = useState(hasTintedText); const [discordSaturation, setDiscordSaturation] = useState(hasDiscordSaturation); const [preset, setPreset] = useState(presetId); - const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar"); - return - Creator Settings - - + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + return
+

Creator Settings

+
+ Presets: - - - {Object.values(getPreset()).map(pre => { - return
-
{ - setPreset(pre.id); - }}> - - {pre.name} -
-
; - })} -
- Use colored text - Use Discord's saturation - - -
+
+ - - - ; + +
+
; } diff --git a/src/equicordplugins/discordColorways/components/ColorwayID.tsx b/src/equicordplugins/discordColorways/components/ColorwayID.tsx new file mode 100644 index 00000000..43c835e1 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/ColorwayID.tsx @@ -0,0 +1,117 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore, openModal, Toasts, useEffect, useState } from ".."; +import { ColorwayCSS } from "../colorwaysAPI"; +import { generateCss } from "../css"; +import { colorToHex, hexToString } from "../utils"; +import CreatorModal from "./CreatorModal"; + +export let changeThemeIDCard: (theme: string) => void = () => { }; + +export default function ({ props }) { + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + changeThemeIDCard = theme => setTheme(theme); + load(); + return () => { + changeThemeIDCard = () => { }; + }; + }, []); + if (String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)) { + return
+ {String(props.message.content).match(/colorway:[0-9a-f]{0,100}/g)?.map((colorID: string) => { + colorID = hexToString(colorID.split("colorway:")[1]); + return
+
+ {(() => { + if (colorID) { + if (!colorID.includes(",")) { + throw new Error("Invalid Colorway ID"); + } else { + return colorID.split("|").filter(string => string.includes(",#"))[0].split(/,#/).map((color: string) =>
); + } + } else return null; + })()} +
+
+ Colorway{/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID) ? `: ${/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID)![1]}` : ""} +
+ + + +
+
+
; + })} +
; + } else { + return null; + } +} diff --git a/src/equicordplugins/discordColorways/components/ColorwaysButton.tsx b/src/equicordplugins/discordColorways/components/ColorwaysButton.tsx index cc8923f8..93e59d21 100644 --- a/src/equicordplugins/discordColorways/components/ColorwaysButton.tsx +++ b/src/equicordplugins/discordColorways/components/ColorwaysButton.tsx @@ -4,15 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import * as DataStore from "@api/DataStore"; -import { openModal } from "@utils/modal"; -import { FluxDispatcher, Text, Tooltip, useEffect, useState } from "@webpack/common"; -import { FluxEvents } from "@webpack/types"; - +import { DataStore, FluxDispatcher, FluxEvents, openModal, useEffect, useState } from ".."; import { getAutoPresets } from "../css"; import { ColorwayObject } from "../types"; import { PalleteIcon } from "./Icons"; -import Selector from "./Selector"; +import Selector from "./MainModal"; +import Tooltip from "./Tooltip"; export default function () { const [activeColorway, setActiveColorway] = useState("None"); @@ -39,11 +36,11 @@ export default function () { <> {!isThin ? <> Colorways - {"Active Colorway: " + activeColorway} + {"Active Colorway: " + activeColorway} : {"Active Colorway: " + activeColorway}} - {activeColorway === "Auto" ? {"Auto Preset: " + (getAutoPresets()[autoPreset].name || "None")} : <>} + {activeColorway === "Auto" ? {"Auto Preset: " + (getAutoPresets()[autoPreset].name || "None")} : <>} - } position="right" tooltipContentClassName="colorwaysBtn-tooltipContent" + } position="right" > {({ onMouseEnter, onMouseLeave, onClick }) => visibility ?
); }} - >{isThin ? Colorways : }
+ >{isThin ? Colorways : }
: <>} ; } diff --git a/src/equicordplugins/discordColorways/components/ConflictingColorsModal.tsx b/src/equicordplugins/discordColorways/components/ConflictingColorsModal.tsx index 410f9aee..e6bc1c52 100644 --- a/src/equicordplugins/discordColorways/components/ConflictingColorsModal.tsx +++ b/src/equicordplugins/discordColorways/components/ConflictingColorsModal.tsx @@ -4,10 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal"; -import { Button, Forms, ScrollerThin, Text, useState } from "@webpack/common"; - +import { DataStore, useEffect, useState } from ".."; import { knownThemeVars } from "../constants"; +import { ModalProps } from "../types"; import { getFontOnBg, getHex } from "../utils"; export default function ({ @@ -37,15 +36,22 @@ export default function ({ document.body ).getPropertyValue("--background-tertiary") )); - return - - - Conflicting Colors Found - - - - Multiple known themes have been found, select the colors you want to copy from below: - Colors to copy: + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + + return
+

+ Conflicting Colors Found +

+
+ Multiple known themes have been found, select the colors you want to copy from below: + Colors to copy:
Primary
Secondary
@@ -53,12 +59,12 @@ export default function ({
Accent
- +
- Discord + Discord
- {Object.keys(knownThemeVars)[i] + (theme.alt ? " (Main)" : "")} + {Object.keys(knownThemeVars)[i] + (theme.alt ? " (Main)" : "")}
{theme.primary && getComputedStyle(document.body).getPropertyValue(theme.primary).match(/^\d.*%$/) ?
+
- - - - - ; + >Finish +
+
; } diff --git a/src/equicordplugins/discordColorways/components/CreatorModal.tsx b/src/equicordplugins/discordColorways/components/CreatorModal.tsx index 0ea34849..0826378b 100644 --- a/src/equicordplugins/discordColorways/components/CreatorModal.tsx +++ b/src/equicordplugins/discordColorways/components/CreatorModal.tsx @@ -4,30 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { - ModalContent, - ModalFooter, - ModalHeader, - ModalProps, - ModalRoot, - openModal, -} from "@utils/modal"; -import { - Button, - Forms, - Slider, - Text, - TextInput, - useEffect, - UserStore, - useState, -} from "@webpack/common"; - -import { ColorPicker, versionData } from ".."; +import { ColorPicker, DataStore, openModal, PluginProps, Slider, useEffect, useReducer, UserStore, useState } from ".."; import { knownThemeVars } from "../constants"; import { generateCss, getPreset, gradientPresetIds, PrimarySatDiffs, pureGradientBase } from "../css"; -import { Colorway } from "../types"; +import { Colorway, ModalProps } from "../types"; import { colorToHex, getHex, HexToHSL, hexToString } from "../utils"; +import { updateRemoteSources } from "../wsClient"; import ColorwayCreatorSettingsModal from "./ColorwayCreatorSettingsModal"; import ConflictingColorsModal from "./ConflictingColorsModal"; import InputColorwayIdModal from "./InputColorwayIdModal"; @@ -35,61 +17,98 @@ import SaveColorwayModal from "./SaveColorwayModal"; import ThemePreviewCategory from "./ThemePreview"; export default function ({ modalProps, - loadUIProps, + loadUIProps = () => new Promise(() => { }), colorwayID }: { modalProps: ModalProps; loadUIProps?: () => Promise; colorwayID?: string; }) { - const [accentColor, setAccentColor] = useState("5865f2"); - const [primaryColor, setPrimaryColor] = useState("313338"); - const [secondaryColor, setSecondaryColor] = useState("2b2d31"); - const [tertiaryColor, setTertiaryColor] = useState("1e1f22"); + const [colors, updateColors] = useReducer((colors: { + accent: string, + primary: string, + secondary: string, + tertiary: string; + }, action: { + task: "accent" | "primary" | "secondary" | "tertiary" | "all", + color?: string; + colorObj?: { + accent: string, + primary: string, + secondary: string, + tertiary: string; + }; + }) => { + if (action.task === "all") { + return { ...action.colorObj } as { + accent: string, + primary: string, + secondary: string, + tertiary: string; + }; + } else { + return { ...colors, [action.task as "accent" | "primary" | "secondary" | "tertiary"]: action.color } as { + accent: string, + primary: string, + secondary: string, + tertiary: string; + }; + } + }, { + accent: "5865f2", + primary: "313338", + secondary: "2b2d31", + tertiary: "1e1f22" + }); const [colorwayName, setColorwayName] = useState(""); const [tintedText, setTintedText] = useState(true); const [discordSaturation, setDiscordSaturation] = useState(true); const [preset, setPreset] = useState("default"); const [presetColorArray, setPresetColorArray] = useState(["accent", "primary", "secondary", "tertiary"]); - const [mutedTextBrightness, setMutedTextBrightness] = useState(Math.min(HexToHSL("#" + primaryColor)[2] + (3.6 * 3), 100)); + const [mutedTextBrightness, setMutedTextBrightness] = useState(Math.min(HexToHSL("#" + colors.primary)[2] + (3.6 * 3), 100)); + const [theme, setTheme] = useState("discord"); - const colorProps = { - accent: { - get: accentColor, - set: setAccentColor, - name: "Accent" - }, - primary: { - get: primaryColor, - set: setPrimaryColor, - name: "Primary" - }, - secondary: { - get: secondaryColor, - set: setSecondaryColor, - name: "Secondary" - }, - tertiary: { - get: tertiaryColor, - set: setTertiaryColor, - name: "Tertiary" + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); } - }; + load(); + }, []); + + const setColor = [ + "accent", + "primary", + "secondary", + "tertiary" + ] as ("accent" | "primary" | "secondary" | "tertiary")[]; + + const colorProps = [ + { + name: "Accent", + id: "accent" + }, + { + name: "Primary", + id: "primary" + }, + { + name: "Secondary", + id: "secondary" + }, + { + name: "Tertiary", + id: "tertiary" + } + ]; useEffect(() => { if (colorwayID) { if (!colorwayID.includes(",")) { throw new Error("Invalid Colorway ID"); } else { - const setColor = [ - setAccentColor, - setPrimaryColor, - setSecondaryColor, - setTertiaryColor - ]; colorwayID.split("|").forEach((prop: string) => { if (prop.includes(",#")) { - prop.split(/,#/).forEach((color: string, i: number) => setColor[i](colorToHex(color))); + prop.split(/,#/).forEach((color: string, i: number) => updateColors({ task: setColor[i], color: colorToHex(color) })); } if (prop.includes("n:")) { setColorwayName(prop.split("n:")[1]); @@ -115,43 +134,54 @@ export default function ({ }; return ( - - - - Create Colorway - - - - - Name: - - +

Create a Colorway

+
+ Name: + setColorwayName(e.currentTarget.value)} />
- - Colors & Values: - + Colors & Values:
- {presetColorArray.map(presetColor => { + {colorProps.filter(color => presetColorArray.includes(color.id) || Object.keys(getPreset()[preset].calculated! || {}).includes(color.id)).map(presetColor => { return {colorProps[presetColor].name}} - color={parseInt(colorProps[presetColor].get, 16)} + label={{Object.keys(getPreset()[preset].calculated! || {}).includes(presetColor.id) ? (presetColor.name + " (Calculated)") : presetColor.name}} + color={!Object.keys( + getPreset()[preset].calculated! || {} + ).includes(presetColor.id) ? + parseInt(colors[presetColor.id], 16) : + parseInt( + colorToHex( + getPreset( + colors.primary, + colors.secondary, + colors.tertiary, + colors.accent + )[preset].calculated![presetColor.id] + ), + 16 + ) + } onChange={(color: number) => { - let hexColor = color.toString(16); - while (hexColor.length < 6) { - hexColor = "0" + hexColor; + if (!Object.keys(getPreset()[preset].calculated! || {}).includes(presetColor.id)) { + let hexColor = color.toString(16); + while (hexColor.length < 6) { + hexColor = "0" + hexColor; + } + updateColors({ task: presetColor.id as "accent" | "primary" | "secondary" | "tertiary", color: hexColor }); } - colorProps[presetColor].set(hexColor); }} {...colorPickerProps} />; })}
- - Muted Text Brightness: +
+ Muted Text Brightness: )}> - Settings & Presets + Settings & Presets
- - - - - - - - + +
+
); } diff --git a/src/equicordplugins/discordColorways/components/FiltersMenu.tsx b/src/equicordplugins/discordColorways/components/FiltersMenu.tsx new file mode 100644 index 00000000..75f275ae --- /dev/null +++ b/src/equicordplugins/discordColorways/components/FiltersMenu.tsx @@ -0,0 +1,85 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { useEffect, useState } from ".."; +import { SortOptions } from "../types"; +import { SortIcon } from "./Icons"; + +export default function ({ sort, onSortChange }: { sort: SortOptions, onSortChange: (newSort: SortOptions) => void; }) { + const [pos, setPos] = useState({ x: 0, y: 0 }); + const [showMenu, setShowMenu] = useState(false); + + function rightClickContextMenu(e) { + e.stopPropagation(); + window.dispatchEvent(new Event("click")); + setShowMenu(!showMenu); + setPos({ + x: e.currentTarget.getBoundingClientRect().x, + y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8 + }); + } + function onPageClick(this: Window, e: globalThis.MouseEvent) { + setShowMenu(false); + } + + useEffect(() => { + window.addEventListener("click", onPageClick); + return () => { + window.removeEventListener("click", onPageClick); + }; + }, []); + + function onSortChange_internal(newSort: SortOptions) { + onSortChange(newSort); + setShowMenu(false); + } + + return <> + {showMenu ? : null} + + ; +} diff --git a/src/equicordplugins/discordColorways/components/Icons.tsx b/src/equicordplugins/discordColorways/components/Icons.tsx index 62acf0f5..22fb5d92 100644 --- a/src/equicordplugins/discordColorways/components/Icons.tsx +++ b/src/equicordplugins/discordColorways/components/Icons.tsx @@ -1,26 +1,35 @@ /* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ -import { classes } from "@utils/misc"; -import type { PropsWithChildren, SVGProps } from "react"; +import type { PropsWithChildren } from "react"; + +import { classes } from "../utils"; interface BaseIconProps extends IconProps { viewBox: string; } -interface IconProps extends SVGProps { - className?: string; - height?: string | number; - width?: string | number; -} +type IconProps = JSX.IntrinsicElements["svg"]; function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren) { return ( + + + + + + ); +} + +/** + * Discord's copy icon, as seen in the user popout right of the username when clicking + * your own username in the bottom left user panel + */ +export function CopyIcon(props: IconProps) { return ( + + + + + + ); +} + +/** + * Discord's open external icon, as seen in the user profile connections + */ +export function OpenExternalIcon(props: IconProps) { + return ( + + + + ); +} + +export function ImageIcon(props: IconProps) { + return ( + + + + ); +} + +export function InfoIcon(props: IconProps) { + return ( + + + ); +} + +/** + * Discord's screenshare icon, as seen in the connection panel + */ +export function ScreenshareIcon(props: IconProps) { + return ( + + + + ); +} + +export function ImageVisible(props: IconProps) { + return ( + + + + ); +} + +export function ImageInvisible(props: IconProps) { + return ( + + + + ); +} + +export function Microphone(props: IconProps) { + return ( + + + + + ); +} + +export function CogWheel(props: IconProps) { + return ( + + + + ); +} + +export function ReplyIcon(props: IconProps) { + return ( + + + + ); +} + +export function DeleteIcon(props: IconProps) { + return ( + + + + + ); +} + +export function SearchIcon(props: IconProps) { + return ( + + + + ); +} +export function PlusIcon(props: IconProps) { + return ( + + ); @@ -51,7 +265,7 @@ export function CloseIcon(props: IconProps) { return ( + + + + ); +} + +export function PalleteIcon(props: IconProps) { + return ( + + + + ); +} + +export function NoEntrySignIcon(props: IconProps) { + return ( + + + + + ); +} + + export function DownloadIcon(props: IconProps) { return ( + + + ); +} + +export function SafetyIcon(props: IconProps) { + return ( + + + + + ); +} + +export function NotesIcon(props: IconProps) { + return ( + ); } + +export function SortIcon(props: IconProps) { + return ( + + + + ); +} + +export function FolderIcon(props: IconProps) { + return ( + + + + ); +} + +export function LogIcon(props: IconProps) { + return ( + + + + ); +} + +export function RestartIcon(props: IconProps) { + return ( + + + + ); +} + +export function PaintbrushIcon(props: IconProps) { + return ( + + + + ); +} + +export function PencilIcon(props: IconProps) { + return ( + + + + ); +} + +export function InternetIcon(props: IconProps) { + return ( + + + + ); +} diff --git a/src/equicordplugins/discordColorways/components/InfoModal.tsx b/src/equicordplugins/discordColorways/components/InfoModal.tsx index 100bfb04..1f2200a9 100644 --- a/src/equicordplugins/discordColorways/components/InfoModal.tsx +++ b/src/equicordplugins/discordColorways/components/InfoModal.tsx @@ -4,54 +4,43 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import * as DataStore from "@api/DataStore"; -import { CodeBlock } from "@components/CodeBlock"; -import { Flex } from "@components/Flex"; -import { - ModalCloseButton, - ModalContent, - ModalFooter, - ModalHeader, - ModalProps, - ModalRoot, - openModal, -} from "@utils/modal"; -import { saveFile } from "@utils/web"; -import { findComponentByCodeLazy } from "@webpack"; -import { Button, Clipboard, Forms, Text, TextInput, Toasts, UserStore, useState, useStateFromStores } from "@webpack/common"; - -import { ColorwayCSS, versionData } from ".."; +import { DataStore, openModal, PluginProps, Toasts, useEffect, UserStore, useState, useStateFromStores } from ".."; +import { ColorwayCSS } from "../colorwaysAPI"; import { generateCss, pureGradientBase } from "../css"; -import { Colorway } from "../types"; -import { colorToHex, stringToHex } from "../utils"; +import { Colorway, ModalProps } from "../types"; +import { colorToHex, saveFile, stringToHex } from "../utils"; import SaveColorwayModal from "./SaveColorwayModal"; import ThemePreview from "./ThemePreview"; -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); - function RenameColorwayModal({ modalProps, ogName, onFinish, colorwayList }: { modalProps: ModalProps, ogName: string, onFinish: (name: string) => void, colorwayList: Colorway[]; }) { const [error, setError] = useState(""); const [newName, setNewName] = useState(ogName); - return - - - Rename Colorway... - - modalProps.onClose()} /> - - - { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + + return
+

+ Rename Colorway... +

+
+ { + setNewName(value); + }} /> - - - - - - ; + +
+
; } export default function ({ @@ -94,42 +80,48 @@ export default function ({ "tertiary", ]; const profile = useStateFromStores([UserStore], () => UserStore.getUser(colorway.authorID)); - return - - - Colorway: {colorway.name} - - modalProps.onClose()} /> - - - - Creator: - - - {colorway.author} - - Colors: - + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + + return
+

+ Colorway: {colorway.name} +

+
+
+ Creator: +
+ {} + { + navigator.clipboard.writeText(profile.username); + Toasts.show({ + message: "Copied Colorway Author Username Successfully", + type: 1, + id: "copy-colorway-author-username-notify", + }); + }}>{colorway.author} +
+ Colors: +
{colors.map(color =>
)} - - Actions: - - - - - {colorway.sourceType === "offline" && } - - - - {colorway.sourceType === "offline" && } - - -
- - ; + } +
+
+
+
; } diff --git a/src/equicordplugins/discordColorways/components/InputColorwayIdModal.tsx b/src/equicordplugins/discordColorways/components/InputColorwayIdModal.tsx index 6100529c..8cbd68a0 100644 --- a/src/equicordplugins/discordColorways/components/InputColorwayIdModal.tsx +++ b/src/equicordplugins/discordColorways/components/InputColorwayIdModal.tsx @@ -4,24 +4,33 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ModalContent, ModalFooter, ModalProps, ModalRoot } from "@utils/modal"; -import { Button, Forms, TextInput, useState } from "@webpack/common"; - +import { DataStore, useEffect, useState } from ".."; +import { ModalProps } from "../types"; import { hexToString } from "../utils"; export default function ({ modalProps, onColorwayId }: { modalProps: ModalProps, onColorwayId: (colorwayID: string) => void; }) { const [colorwayID, setColorwayID] = useState(""); - return - - Colorway ID: - setColorwayID(e.currentTarget.value)} /> - - - - - - ; + +
+
; } diff --git a/src/equicordplugins/discordColorways/components/MainModal.tsx b/src/equicordplugins/discordColorways/components/MainModal.tsx new file mode 100644 index 00000000..189b61a8 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/MainModal.tsx @@ -0,0 +1,114 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { MouseEvent, MouseEventHandler } from "react"; + +import { DataStore, useEffect, useRef, useState } from "../"; +import { ModalProps } from "../types"; +import { restartWS, updateRemoteSources, wsOpen } from "../wsClient"; +// eslint-disable-next-line no-duplicate-imports +import { boundKey as bk } from "../wsClient"; +import Selector from "./Selector"; +import SettingsPage from "./SettingsTabs/SettingsPage"; +import SourceManager from "./SettingsTabs/SourceManager"; +import Store from "./SettingsTabs/Store"; + +export let changeTheme = (theme: string) => { }; +export let updateWSMain: (status: boolean) => void = () => { }; +export let updateBoundKeyMain: (boundKey: { [managerKey: string]: string; }) => void = () => { }; + +export default function ({ + modalProps +}: { + modalProps: ModalProps; +}): JSX.Element | any { + const [activeTab, setActiveTab] = useState<"selector" | "settings" | "sources" | "store" | "ws_connection">("selector"); + const [theme, setTheme] = useState("discord"); + const [pos, setPos] = useState({ x: 0, y: 0 }); + const [showMenu, setShowMenu] = useState(false); + const [wsConnected, setWsConnected] = useState(wsOpen); + const [boundKey, setBoundKey] = useState<{ [managerKey: string]: string; }>(bk as { [managerKey: string]: string; }); + const menuProps = useRef(null); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + updateWSMain = status => setWsConnected(status); + changeTheme = (theme: string) => setTheme(theme); + updateBoundKeyMain = bound => setBoundKey(bound); + load(); + + return () => { + updateWSMain = () => { }; + changeTheme = () => { }; + updateBoundKeyMain = () => { }; + }; + }, []); + + function SidebarTab({ id, title, icon, bottom }: { id: "selector" | "settings" | "sources" | "store" | "ws_connection", title: string, icon: JSX.Element, bottom?: boolean; }) { + return
setActiveTab(id)) as unknown as MouseEventHandler) : rightClickContextMenu}>{icon}
; + } + + const rightClickContextMenu: MouseEventHandler = (e: MouseEvent) => { + e.stopPropagation(); + window.dispatchEvent(new Event("click")); + setShowMenu(!showMenu); + setPos({ + x: e.currentTarget.getBoundingClientRect().x + e.currentTarget.offsetWidth + 8, + y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight - (menuProps.current as unknown as HTMLElement).offsetHeight + }); + }; + function onPageClick(this: Window, e: globalThis.MouseEvent) { + setShowMenu(false); + } + + useEffect(() => { + window.addEventListener("click", onPageClick); + return () => { + window.removeEventListener("click", onPageClick); + }; + }, []); + + return ( + <> +
+
+ } id="selector" title="Change Colorway" /> + } id="settings" title="Settings" /> + } id="sources" title="Sources" /> + } id="store" title="Store" /> + } id="ws_connection" title="Manager Connection" /> +
+
+ {activeTab === "selector" && } + {activeTab === "sources" && } + {activeTab === "store" && } + {activeTab === "settings" &&
} +
+
+ Manager Connection Status: {wsConnected ? "Connected" : "Disconnected"} + {wsConnected ? <> + Bound Key: {JSON.stringify(boundKey)} + + + + : <>} +
+
+ + ); +} diff --git a/src/equicordplugins/discordColorways/components/PCSMigrationModal.tsx b/src/equicordplugins/discordColorways/components/PCSMigrationModal.tsx new file mode 100644 index 00000000..47a01049 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/PCSMigrationModal.tsx @@ -0,0 +1,35 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore, useEffect, useState } from ".."; +import { ModalProps } from "../types"; + +export default function ({ modalProps }: { modalProps: ModalProps; }) { + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + return
+

+ Project Colorway has moved +

+
+ + In the process of creating a more solid foundation + for Project Colorway, the main Project Colorway repository has been + moved from https://github.com/DaBluLite/ProjectColorway to{" "} + https://github.com/ProjectColorway/ProjectColorway + +
+ The default Project Colorway source has been automatically updated/re-added. +
+
+
; +} diff --git a/src/equicordplugins/discordColorways/components/ReloadButton.tsx b/src/equicordplugins/discordColorways/components/ReloadButton.tsx new file mode 100644 index 00000000..c0acde87 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/ReloadButton.tsx @@ -0,0 +1,99 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { useEffect, useRef, useState } from ".."; + +export default function ({ onClick, onForceReload }: { onClick: () => void, onForceReload: () => void; }) { + const menuProps = useRef(null); + const [pos, setPos] = useState({ x: 0, y: 0 }); + const [showMenu, setShowMenu] = useState(false); + + function rightClickContextMenu(e) { + e.stopPropagation(); + window.dispatchEvent(new Event("click")); + setShowMenu(!showMenu); + setPos({ + x: e.currentTarget.getBoundingClientRect().x, + y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8 + }); + } + function onPageClick(this: Window, e: globalThis.MouseEvent) { + setShowMenu(false); + } + + useEffect(() => { + window.addEventListener("click", onPageClick); + return () => { + window.removeEventListener("click", onPageClick); + }; + }, []); + + function onForceReload_internal() { + onForceReload(); + setShowMenu(false); + } + + return <> + {showMenu ? : null} + + ; +} diff --git a/src/equicordplugins/discordColorways/components/SaveColorwayModal.tsx b/src/equicordplugins/discordColorways/components/SaveColorwayModal.tsx index 8a034edb..6f09d906 100644 --- a/src/equicordplugins/discordColorways/components/SaveColorwayModal.tsx +++ b/src/equicordplugins/discordColorways/components/SaveColorwayModal.tsx @@ -4,67 +4,63 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { DataStore } from "@api/index"; -import { PlusIcon } from "@components/Icons"; -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { Button, Text, TextInput, useEffect, useState } from "@webpack/common"; - -import { Colorway } from "../types"; +import { DataStore, openModal, useEffect, useState } from ".."; +import { Colorway, ModalProps } from "../types"; +import { PlusIcon } from "./Icons"; import { StoreNameModal } from "./SettingsTabs/SourceManager"; export default function ({ modalProps, colorways, onFinish }: { modalProps: ModalProps, colorways: Colorway[], onFinish: () => void; }) { const [offlineColorwayStores, setOfflineColorwayStores] = useState<{ name: string, colorways: Colorway[], id?: string; }[]>([]); const [storename, setStorename] = useState(); const [noStoreError, setNoStoreError] = useState(false); - const { radioBar, item: radioBarItem, itemFilled: radioBarItemFilled, radioPositionLeft } = findByProps("radioBar"); useEffect(() => { (async () => { setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[], id?: string; }[]); })(); }); - return - - Select Offline Colorway Source - - - {noStoreError ? Error: No store selected : <>} - {offlineColorwayStores.map(store => { - return
-
{ - setStorename(store.name); - }}> - - {store.name} -
-
; - })} -
-
{ - openModal(props => { - await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]); - setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]); - }} />); - }}> - - Create new store... -
+ const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + return
+

+ Save to source: +

+
+ {noStoreError ? Error: No store selected : <>} + {offlineColorwayStores.map(store =>
{ + setStorename(store.name); + }}> + + {store.name} +
)} +
{ + openModal(props => { + await DataStore.set("customColorways", [...await DataStore.get("customColorways"), { name: e, colorways: [] }]); + setOfflineColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]); + }} />); + }}> + + Create new store...
- - - - - - - ; + +
+
; } openModal(propss => { const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, { ...colorway, name: e }] }; @@ -163,20 +152,17 @@ export default function ({ modalProps, colorways, onFinish }: { modalProps: Moda }} > Rename - - - - ); + +
+
); } else { const newStore = { name: storeToModify.name, colorways: [...storeToModify.colorways, colorway] }; DataStore.set("customColorways", [...oldStores!.filter(source => source.name !== storename), newStore]); @@ -190,18 +176,15 @@ export default function ({ modalProps, colorways, onFinish }: { modalProps: Moda }} > Finish - - - - ; + +
+
; } diff --git a/src/equicordplugins/discordColorways/components/SelectionCircle.tsx b/src/equicordplugins/discordColorways/components/SelectionCircle.tsx deleted file mode 100644 index 22e919f2..00000000 --- a/src/equicordplugins/discordColorways/components/SelectionCircle.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export default function () { - return
- -
; -} diff --git a/src/equicordplugins/discordColorways/components/Selector.tsx b/src/equicordplugins/discordColorways/components/Selector.tsx index 29cf6ff9..7e0e0968 100644 --- a/src/equicordplugins/discordColorways/components/Selector.tsx +++ b/src/equicordplugins/discordColorways/components/Selector.tsx @@ -1,103 +1,70 @@ /* * Vencord, a Discord client mod - * Copyright (c) 2023 Vendicated and contributors + * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ -import * as DataStore from "@api/DataStore"; -import { Flex } from "@components/Flex"; -import { DeleteIcon, PlusIcon } from "@components/Icons"; -import { SettingsTab } from "@components/VencordSettings/shared"; -import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { - Button, - ButtonLooks, - Clipboard, - Forms, - Menu, - Popout, - ScrollerThin, - Select, - SettingsRouter, - Text, - TextInput, - Toasts, - Tooltip, - useEffect, - useState -} from "@webpack/common"; -import { ReactNode } from "react"; - -import { ColorwayCSS } from ".."; +import { DataStore, ReactNode, Toasts } from ".."; +import { openModal, useEffect, useState } from "../"; +import { ColorwayCSS } from "../colorwaysAPI"; +import { nullColorwayObj } from "../constants"; import { generateCss, getAutoPresets, gradientBase } from "../css"; -import { Colorway, ColorwayObject, SortOptions, SourceObject } from "../types"; +import { Colorway, ColorwayObject, ModalProps, SortOptions, SourceObject } from "../types"; import { colorToHex, getHex, stringToHex } from "../utils"; +import { hasManagerRole, requestManagerRole, sendColorway, updateRemoteSources, wsOpen } from "../wsClient"; import AutoColorwaySelector from "./AutoColorwaySelector"; -import ColorPickerModal from "./ColorPicker"; import CreatorModal from "./CreatorModal"; -import { CodeIcon, IDIcon, MoreIcon, PalleteIcon } from "./Icons"; -import ColorwayInfoModal from "./InfoModal"; -import SelectionCircle from "./SelectionCircle"; +import FiltersMenu from "./FiltersMenu"; +import { CodeIcon, DeleteIcon, IDIcon, PalleteIcon, PlusIcon } from "./Icons"; +import InfoModal from "./InfoModal"; +import ReloadButton from "./ReloadButton"; +import SourcesMenu from "./SourcesMenu"; +import UseRepainterThemeModal from "./UseRepainterThemeModal"; -function SelectorContainer({ children, isSettings, modalProps }: { children: ReactNode, isSettings?: boolean, modalProps: ModalProps; }) { - if (!isSettings) { - return - {children} - ; - } else { - return -
- {children} -
-
; - } -} - -function SelectorHeader({ children, isSettings }: { children: ReactNode, isSettings?: boolean; }) { - if (!isSettings) { - return - {children} - ; - } else { - return - {children} - ; - } -} - -function SelectorContent({ children, isSettings }: { children: ReactNode, isSettings?: boolean; }) { - if (!isSettings) { - return {children}; - } else { - return <>{children}; - } -} +export let updateWS: (status: boolean) => void = () => { }; +export let updateManagerRole: (hasManager: boolean) => void = () => { }; +export let updateActiveColorway: (active: ColorwayObject) => void = () => { }; export default function ({ - modalProps, - isSettings, - settings = { selectorType: "normal" } + settings = { selectorType: "normal" }, + hasTheme = false }: { - modalProps: ModalProps, - isSettings?: boolean, settings?: { selectorType: "preview" | "multiple-selection" | "normal", previewSource?: string, onSelected?: (colorways: Colorway[]) => void; }; -}): JSX.Element | any { + hasTheme?: boolean; +}) { const [colorwayData, setColorwayData] = useState([]); const [searchValue, setSearchValue] = useState(""); const [sortBy, setSortBy] = useState(SortOptions.NAME_AZ); - const [activeColorwayObject, setActiveColorwayObject] = useState({ id: null, css: null, sourceType: null, source: null }); + const [activeColorwayObject, setActiveColorwayObject] = useState(nullColorwayObj); const [customColorwayData, setCustomColorwayData] = useState([]); const [loaderHeight, setLoaderHeight] = useState<"2px" | "0px">("2px"); const [visibleSources, setVisibleSources] = useState("all"); - const [showReloadMenu, setShowReloadMenu] = useState(false); - const [viewMode, setViewMode] = useState<"list" | "grid">("grid"); - const [showLabelsInSelectorGridView, setShowLabelsInSelectorGridView] = useState(false); - const [showSortingMenu, setShowSotringMenu] = useState(false); const [selectedColorways, setSelectedColorways] = useState([]); const [errorCode, setErrorCode] = useState(0); + const [wsConnected, setWsConnected] = useState(wsOpen); + const [theme, setTheme] = useState("discord"); + const [isManager, setManager] = useState(hasManagerRole); - const { item: radioBarItem, itemFilled: radioBarItemFilled } = findByProps("radioBar"); + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + updateWS = status => { + setWsConnected(status); + }; + console.log(hasManagerRole); + updateManagerRole = hasManager => { + setManager(hasManager); + console.log(hasManager); + }; + updateActiveColorway = setActiveColorwayObject; + return () => { + updateWS = () => { }; + updateManagerRole = () => { }; + updateActiveColorway = () => { }; + }; + }, [isManager]); const filters = [ { @@ -119,8 +86,6 @@ export default function ({ async function loadUI(force?: boolean) { setActiveColorwayObject(await DataStore.get("activeColorwayObject") as ColorwayObject); - setViewMode(await DataStore.get("selectorViewMode") as "list" | "grid"); - setShowLabelsInSelectorGridView(await DataStore.get("showLabelsInSelectorGridView") as boolean); setLoaderHeight("0px"); if (settings.previewSource) { @@ -161,676 +126,438 @@ export default function ({ useEffect(() => { loadUI(); }, [searchValue]); - function ReloadPopout(onClose: () => void) { - return ( - - loadUI(true)} - /> - - ); + function Header({ children }: { children: ReactNode; }) { + if (hasTheme) return
{children}
; + else return
{children}
; } - function SortingPopout(onClose: () => void) { - return ( - - - { - setViewMode("grid"); - DataStore.set("selectorViewMode", "grid"); - }} - /> - { - setViewMode("list"); - DataStore.set("selectorViewMode", "list"); - }} - /> - - - setSortBy(SortOptions.NAME_AZ)} - /> - setSortBy(SortOptions.NAME_ZA)} - /> - setSortBy(SortOptions.SOURCE_AZ)} - /> - setSortBy(SortOptions.SOURCE_ZA)} - /> - - - ); + function Container({ children }: { children: ReactNode; }) { + if (hasTheme) return
{children}
; + else return
{children}
; } - return ( - - - {settings.selectorType !== "preview" ? <> - - - {({ onMouseEnter, onMouseLeave }) => setShowReloadMenu(false)} - renderPopout={() => ReloadPopout(() => setShowReloadMenu(false))} - > - {(_, { isShown }) => } - } - - - {({ onMouseEnter, onMouseLeave }) => } - - - {({ onMouseEnter, onMouseLeave }) => setShowSotringMenu(false)} - renderPopout={() => SortingPopout(() => setShowSotringMenu(false))} - > - {(_, { isShown }) => } - } - - - {({ onMouseEnter, onMouseLeave }) => } - - {isSettings ? setSearchValue(value)} + /> +
+ { + setLoaderHeight("2px"); + loadUI().then(() => setLoaderHeight("0px")); + }} onForceReload={() => { + setLoaderHeight("2px"); + loadUI(true).then(() => setLoaderHeight("0px")); + }} /> + + + { + setSortBy(newSort); + }} /> + filter.id === visibleSources)[0]} sources={filters} onSourceChange={sourceId => { + setVisibleSources(sourceId); + }} /> +
+ : <>} + {(wsConnected && settings.selectorType === "normal" && !isManager) ? + + + + Manager is controlling the colorways + + : <> +
+ + {(activeColorwayObject.sourceType === "temporary" && settings.selectorType === "normal" && settings.selectorType === "normal") &&
{ + DataStore.set("activeColorwayObject", nullColorwayObj); + setActiveColorwayObject(nullColorwayObj); + ColorwayCSS.remove(); }} > - Settings - - +
} + {getComputedStyle(document.body).getPropertyValue("--os-accent-color") && ["all", "official"].includes(visibleSources) && settings.selectorType === "normal" && "auto".includes(searchValue.toLowerCase()) ?
{ + if (isManager) { + Toasts.show({ + message: "Cannot use Auto colorway while on manager mode", + type: 2, + id: "colorways-manager-role-auto-colorway-error" + }); + } else { + const activeAutoPreset = await DataStore.get("activeAutoPreset"); + if (activeColorwayObject.id === "Auto") { + DataStore.set("activeColorwayObject", nullColorwayObj); + setActiveColorwayObject(nullColorwayObj); + ColorwayCSS.remove(); + } else { + if (!activeAutoPreset) { + openModal((props: ModalProps) => { + const demandedColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6))[autoPresetId].preset(); + ColorwayCSS.set(demandedColorway); + const newObj: ColorwayObject = { + id: "Auto", + css: demandedColorway, + sourceType: "online", + source: null, + colors: { + accent: colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6) + } + }; + DataStore.set("activeColorwayObject", newObj); + setActiveColorwayObject(newObj); + }} />); + } else { + const autoColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6))[activeAutoPreset].preset(); + const newObj: ColorwayObject = { + id: "Auto", + css: autoColorway, + sourceType: "online", + source: null, + colors: { + accent: colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")).slice(0, 6) + } + }; + DataStore.set("activeColorwayObject", newObj); + setActiveColorwayObject(newObj); + ColorwayCSS.set(autoColorway); + } + } + } + }} > - Close - - { + setTheme(e.currentTarget.value); + DataStore.set("colorwaysPluginTheme", e.currentTarget.value); + changeTheme(e.currentTarget.value); + }} + value={theme} + > + + + +
+ + Manager + +
+ + +
+
+ +
+ + +
+
+ +
+ + +
+ Reset the plugin to its default settings. All bound managers, sources, and colorways will be deleted. Please reload Discord after use. +
+
+

+ Discord - Discord Colorways -

- by Project Colorway - - Plugin Version: - - - {versionData.pluginVersion} - - - Creator Version: - - - {versionData.creatorVersion}{" (Stable)"} - - - Loaded Colorways: - - - {[...colorways, ...customColorways].length + 1} - - - Project Repositories: - - - DiscordColorways -
- Project Colorway -
- + backgroundColor: "var(--brand-500)", + padding: "0 4px", + borderRadius: "4px" + }}>Colorways + + by Project Colorway + + Plugin Version: + + + {PluginProps.pluginVersion} ({PluginProps.clientMod}) + + + UI Version: + + + {PluginProps.UIVersion} + + + Creator Version: + + + {PluginProps.creatorVersion} + + + Loaded Colorways: + + + {[...colorways, ...customColorways].length} + + + Project Repositories: + + DiscordColorways + Project Colorway
- ; +
; } diff --git a/src/equicordplugins/discordColorways/components/SettingsTabs/SourceManager.tsx b/src/equicordplugins/discordColorways/components/SettingsTabs/SourceManager.tsx index 2cc3eeb9..7d7e2a9c 100644 --- a/src/equicordplugins/discordColorways/components/SettingsTabs/SourceManager.tsx +++ b/src/equicordplugins/discordColorways/components/SettingsTabs/SourceManager.tsx @@ -4,39 +4,39 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { DataStore } from "@api/index"; -import { Flex } from "@components/Flex"; -import { CopyIcon, DeleteIcon, PlusIcon } from "@components/Icons"; -import { SettingsTab } from "@components/VencordSettings/shared"; -import { Logger } from "@utils/Logger"; -import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; -import { chooseFile, saveFile } from "@utils/web"; -import { findByProps } from "@webpack"; -import { Button, Clipboard, Forms, ScrollerThin, Text, TextInput, useEffect, useState } from "@webpack/common"; - +import { DataStore, openModal, ReactNode, useEffect, useState } from "../../"; import { defaultColorwaySource } from "../../constants"; -import { Colorway } from "../../types"; -import { DownloadIcon, ImportIcon } from "../Icons"; -import Spinner from "../Spinner"; +import { Colorway, ModalProps } from "../../types"; +import { chooseFile, saveFile } from "../../utils"; +import { updateRemoteSources } from "../../wsClient"; +import { CopyIcon, DeleteIcon, DownloadIcon, ImportIcon, PlusIcon } from "../Icons"; +import TabBar from "../TabBar"; export function StoreNameModal({ modalProps, originalName, onFinish, conflicting }: { modalProps: ModalProps, originalName: string, onFinish: (newName: string) => Promise, conflicting: boolean; }) { const [error, setError] = useState(""); const [newStoreName, setNewStoreName] = useState(originalName); - return - - {conflicting ? "Duplicate Store Name" : "Give this store a name"} - - - {conflicting ? A store with the same name already exists. Please give a different name to the imported store: : <>} - Name: - setNewStoreName(e)} style={{ marginBottom: "16px" }} /> - - - - - - ; + +
+
; } function AddOnlineStoreModal({ modalProps, onFinish }: { modalProps: ModalProps, onFinish: (name: string, url: string) => void; }) { @@ -67,26 +65,35 @@ function AddOnlineStoreModal({ modalProps, onFinish }: { modalProps: ModalProps, const [nameError, setNameError] = useState(""); const [URLError, setURLError] = useState(""); const [nameReadOnly, setNameReadOnly] = useState(false); - return - - - Add a source: - - - - Name: - { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + return
+

+ Add a source: +

+
+ Name: + setColorwaySourceName(e.currentTarget.value)} value={colorwaySourceName} - error={nameError} readOnly={nameReadOnly} disabled={nameReadOnly} /> - URL: - URL: + { + onChange={({ currentTarget: { value } }) => { setColorwaySourceURL(value); if (value === defaultColorwaySource) { setNameReadOnly(true); @@ -94,16 +101,12 @@ function AddOnlineStoreModal({ modalProps, onFinish }: { modalProps: ModalProps, } }} value={colorwaySourceURL} - error={URLError} style={{ marginBottom: "16px" }} /> - - - - - - ; + +
+
; } -export default function () { - const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]); - const [customColorwayStores, setCustomColorwayStores] = useState<{ name: string, colorways: Colorway[]; }[]>([]); - - const { item: radioBarItem, itemFilled: radioBarItemFilled } = findByProps("radioBar"); +export default function ({ + hasTheme = false +}: { + hasTheme?: boolean; +}) { + const [theme, setTheme] = useState("discord"); + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + + function Container({ children }: { children: ReactNode; }) { + if (hasTheme) return
{children}
; + else return
{children}
; + } + + return + + ; +} + +function OfflineTab() { + const [customColorwayStores, setCustomColorwayStores] = useState<{ name: string, colorways: Colorway[]; }[]>([]); useEffect(() => { (async function () { - setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]); setCustomColorwayStores(await DataStore.get("customColorways") as { name: string, colorways: Colorway[]; }[]); + updateRemoteSources(); })(); }, []); - return - - Online - - - - {!colorwaySourceFiles.length &&
{ - DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }]); - setColorwaySourceFiles([{ name: "Project Colorway", url: defaultColorwaySource }]); - }}> - - - Add Project Colorway Source - -
} - {colorwaySourceFiles.map((colorwaySourceFile: { name: string, url: string; }, i: number) =>
-
- - {colorwaySourceFile.name} {colorwaySourceFile.url === defaultColorwaySource &&
Built-In
} -
- - {colorwaySourceFile.url} - -
- - - {colorwaySourceFile.url !== defaultColorwaySource - && <> - - - } - -
- )} -
- - Offline - - - - - {getComputedStyle(document.body).getPropertyValue("--os-accent-color") ?
- - OS Accent Color{" "} + +
+
+ {getComputedStyle(document.body).getPropertyValue("--os-accent-color") ?
+
+ OS Accent Color{" "}
Built-In
- - +
+
: <>} - {customColorwayStores.map(({ name: customColorwaySourceName, colorways: offlineStoreColorways }) =>
- + {customColorwayStores.map(({ name: customColorwaySourceName, colorways: offlineStoreColorways }) =>
+ {customColorwaySourceName} - - - - - + +
)} - - ; +
+
; +} + +function OnlineTab() { + const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]); + useEffect(() => { + (async function () { + setColorwaySourceFiles(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]); + updateRemoteSources(); + })(); + }, []); + return
+
+ +
+
+ {!colorwaySourceFiles.length &&
{ + DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]); + setColorwaySourceFiles([{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]); + }}> + + + Add Project Colorway Source + +
} + {colorwaySourceFiles.map((colorwaySourceFile: { name: string, url: string; }, i: number) =>
+
+ + {colorwaySourceFile.name} {colorwaySourceFile.url === defaultColorwaySource &&
Built-In
} {colorwaySourceFile.url === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" &&
Built-In | Outdated
} +
+ + {colorwaySourceFile.url} + +
+
+ + {colorwaySourceFile.url === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" && } + {(colorwaySourceFile.url !== defaultColorwaySource && colorwaySourceFile.url !== "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json") + && <> + + + } +
+
+ )} +
+
; } diff --git a/src/equicordplugins/discordColorways/components/SettingsTabs/Store.tsx b/src/equicordplugins/discordColorways/components/SettingsTabs/Store.tsx index c3e141cd..49a2cb36 100644 --- a/src/equicordplugins/discordColorways/components/SettingsTabs/Store.tsx +++ b/src/equicordplugins/discordColorways/components/SettingsTabs/Store.tsx @@ -4,32 +4,27 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { DataStore } from "@api/index"; -import { Flex } from "@components/Flex"; -import { DeleteIcon } from "@components/Icons"; -import { Link } from "@components/Link"; -import { SettingsTab } from "@components/VencordSettings/shared"; -import { getTheme, Theme } from "@utils/discord"; -import { openModal } from "@utils/modal"; -import { findByProps } from "@webpack"; -import { Button, ScrollerThin, Text, TextInput, Tooltip, useEffect, useState } from "@webpack/common"; - +import { DataStore, openModal, ReactNode, useEffect, useState } from "../../"; import { StoreItem } from "../../types"; -import { DownloadIcon, PalleteIcon } from "../Icons"; +import { DeleteIcon, DownloadIcon, PalleteIcon } from "../Icons"; import Selector from "../Selector"; -const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg"; -const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg"; - -function GithubIcon() { - const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark; - return GitHub; -} - -export default function () { +export default function ({ + hasTheme = false +}: { + hasTheme?: boolean; +}) { const [storeObject, setStoreObject] = useState([]); const [colorwaySourceFiles, setColorwaySourceFiles] = useState<{ name: string, url: string; }[]>([]); const [searchValue, setSearchValue] = useState(""); + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); useEffect(() => { if (!searchValue) { @@ -42,79 +37,76 @@ export default function () { } }, []); - const { item: radioBarItem, itemFilled: radioBarItemFilled } = findByProps("radioBar"); + function Container({ children }: { children: ReactNode; }) { + if (hasTheme) return
{children}
; + else return
{children}
; + } - return - - +
+ setSearchValue(e.currentTarget.value)} /> - - {({ onMouseEnter, onMouseLeave }) => } - - - + + + + + Refresh + +
+
{storeObject.map((item: StoreItem) => - item.name.toLowerCase().includes(searchValue.toLowerCase()) ?
- - + item.name.toLowerCase().includes(searchValue.toLowerCase()) ?
+
+ {item.name} - - + + {item.description} - - + + by {item.authorGh} - - - - -
+
+ + GitHub + + - - + + Preview + +
: <> )} - - ; +
+ ; } diff --git a/src/equicordplugins/discordColorways/components/SourcesMenu.tsx b/src/equicordplugins/discordColorways/components/SourcesMenu.tsx new file mode 100644 index 00000000..55142f4b --- /dev/null +++ b/src/equicordplugins/discordColorways/components/SourcesMenu.tsx @@ -0,0 +1,61 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { useEffect, useRef, useState } from ".."; + +export default function ({ source, sources, onSourceChange }: { source: { name: string, id: string; }, sources: { name: string, id: string; }[], onSourceChange: (sourceId: string) => void; }) { + const menuProps = useRef(null); + const [pos, setPos] = useState({ x: 0, y: 0 }); + const [showMenu, setShowMenu] = useState(false); + const [current, setCurrent] = useState(source); + + function rightClickContextMenu(e) { + e.stopPropagation(); + window.dispatchEvent(new Event("click")); + setShowMenu(!showMenu); + setPos({ + x: e.currentTarget.getBoundingClientRect().x, + y: e.currentTarget.getBoundingClientRect().y + e.currentTarget.offsetHeight + 8 + }); + } + function onPageClick() { + setShowMenu(false); + } + + useEffect(() => { + window.addEventListener("click", onPageClick); + return () => { + window.removeEventListener("click", onPageClick); + }; + }, []); + + function onSourceChange_internal(newSort: { name: string, id: string; }) { + onSourceChange(newSort.id); + setCurrent(newSort); + setShowMenu(false); + } + + return <> + {showMenu ? : null} + + ; +} diff --git a/src/equicordplugins/discordColorways/components/Spinner.tsx b/src/equicordplugins/discordColorways/components/Spinner.tsx index 650db512..882f50fc 100644 --- a/src/equicordplugins/discordColorways/components/Spinner.tsx +++ b/src/equicordplugins/discordColorways/components/Spinner.tsx @@ -4,9 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { CSSProperties } from "react"; - -export default function ({ className, style }: { className?: string, style?: CSSProperties; }) { +export default function ({ className, style }: { className?: string, style?: any; }) { return
diff --git a/src/equicordplugins/discordColorways/components/Switch.tsx b/src/equicordplugins/discordColorways/components/Switch.tsx new file mode 100644 index 00000000..8533a278 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/Switch.tsx @@ -0,0 +1,78 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export default function ({ + onChange, + value, + id, + label +}: { + id?: string, + value: boolean, + label?: string, + onChange: (checked: boolean) => void; +}) { + return label ?
+ +
+ + { + onChange(e.currentTarget.checked); + }} /> +
+
:
+ + { + onChange(e.currentTarget.checked); + }} /> +
; +} diff --git a/src/equicordplugins/discordColorways/components/TabBar.tsx b/src/equicordplugins/discordColorways/components/TabBar.tsx new file mode 100644 index 00000000..5e960819 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/TabBar.tsx @@ -0,0 +1,28 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { useState } from ".."; + +export default function ({ + items = [] +}: { + items: { name: string, component: () => JSX.Element; }[]; +}) { + const [active, setActive] = useState(items[0].name); + return <> +
+ {items.map(item => { + return
{ + setActive(item.name); + }}>{item.name}
; + })} +
+ {items.map(item => { + const Component = item.component; + return active === item.name ? : null; + })} + ; +} diff --git a/src/equicordplugins/discordColorways/components/ThemePreview.tsx b/src/equicordplugins/discordColorways/components/ThemePreview.tsx index 1833f8e9..c892d088 100644 --- a/src/equicordplugins/discordColorways/components/ThemePreview.tsx +++ b/src/equicordplugins/discordColorways/components/ThemePreview.tsx @@ -1,12 +1,11 @@ /* * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors + * Copyright (c) 2023 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ -import { ModalProps, ModalRoot, openModal } from "@utils/modal"; -import { Text } from "@webpack/common"; - +import { openModal } from ".."; +import { ModalProps } from "../types"; import { HexToHSL } from "../utils"; import { CloseIcon } from "./Icons"; @@ -48,12 +47,12 @@ export default function ThemePreview({ if (isModal) { modalProps?.onClose(); } else { - openModal((props: ModalProps) => + openModal((props: ModalProps) =>
- ); +
); } }} > @@ -122,14 +121,12 @@ export default function ThemePreview({ "--primary-500-hsl": `${HexToHSL(primary)[0]} ${HexToHSL(primary)[1]}% ${Math.min(HexToHSL(primary)[2] + (3.6 * 3), 100)}%` } as React.CSSProperties} > - + Preview - +
diff --git a/src/equicordplugins/discordColorways/components/Tooltip.tsx b/src/equicordplugins/discordColorways/components/Tooltip.tsx new file mode 100644 index 00000000..28491593 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/Tooltip.tsx @@ -0,0 +1,63 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { useEffect, useRef, useState } from ".."; + +export default function ({ + children, + text, + position = "top" +}: { + children: (props: { onMouseEnter: () => void; onMouseLeave: () => void; onClick: () => void; }) => JSX.Element, + text: JSX.Element, + position?: "top" | "bottom" | "left" | "right"; +}) { + const [visible, setVisible] = useState(false); + const [pos, setPos] = useState({ x: 0, y: 0 }); + const btn = useRef(null); + + function showTooltip() { + setPos({ + x: (btn.current as unknown as HTMLElement).children[0].getBoundingClientRect().x + ((btn.current as unknown as HTMLElement).children[0] as HTMLElement).offsetWidth + 8, + y: (btn.current as unknown as HTMLElement).children[0].getBoundingClientRect().y + }); + setVisible(true); + } + + function onWindowUnfocused(e) { + e = e || window.event; + var from = e.relatedTarget || e.toElement; + if (!from || from.nodeName === "HTML") { + setVisible(false); + } + } + + useEffect(() => { + document.addEventListener("mouseout", onWindowUnfocused); + return () => { + document.removeEventListener("mouseout", onWindowUnfocused); + }; + }, []); + + return <> +
+ {children({ + onMouseEnter: () => showTooltip(), + onMouseLeave: () => setVisible(false), + onClick: () => setVisible(false) + })} +
+
+
+
{text}
+
+ ; +} diff --git a/src/equicordplugins/discordColorways/components/UseRepainterThemeModal.tsx b/src/equicordplugins/discordColorways/components/UseRepainterThemeModal.tsx new file mode 100644 index 00000000..f9a01d02 --- /dev/null +++ b/src/equicordplugins/discordColorways/components/UseRepainterThemeModal.tsx @@ -0,0 +1,57 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore, useEffect, useState } from ".."; +import { ModalProps } from "../types"; +import { getRepainterTheme } from "../utils"; + +export default function ({ modalProps, onFinish }: { modalProps: ModalProps, onFinish: ({ id, colors }: { id: string, colors: string[]; }) => void; }) { + const [colorwaySourceURL, setColorwaySourceURL] = useState(""); + const [URLError, setURLError] = useState(""); + const [theme, setTheme] = useState("discord"); + + useEffect(() => { + async function load() { + setTheme(await DataStore.get("colorwaysPluginTheme") as string); + } + load(); + }, []); + + return
+

Use Repainter theme

+
+ URL: {URLError ? {URLError} : <>} + { + setColorwaySourceURL(e.currentTarget.value); + }} + value={colorwaySourceURL} + className="colorwaySelector-search" + /> +
+
+ + +
+
; +} diff --git a/src/equicordplugins/discordColorways/constants.ts b/src/equicordplugins/discordColorways/constants.ts index fdcdf90c..516a267c 100644 --- a/src/equicordplugins/discordColorways/constants.ts +++ b/src/equicordplugins/discordColorways/constants.ts @@ -4,7 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -export const defaultColorwaySource = "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json"; +import { ColorwayObject } from "./types"; + +export const defaultColorwaySource = "https://raw.githubusercontent.com/ProjectColorway/ProjectColorway/master/index.json"; export const fallbackColorways = [ { @@ -311,3 +313,5 @@ export const mainColors = [ { name: "secondary", title: "Secondary", var: "--background-secondary" }, { name: "tertiary", title: "Tertiary", var: "--background-tertiary" } ]; + +export const nullColorwayObj: ColorwayObject = { id: null, css: null, sourceType: null, source: null }; diff --git a/src/equicordplugins/discordColorways/css.ts b/src/equicordplugins/discordColorways/css.ts index 565b808e..1eb19046 100644 --- a/src/equicordplugins/discordColorways/css.ts +++ b/src/equicordplugins/discordColorways/css.ts @@ -4,9 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { UserStore } from "@webpack/common"; -import { Plugins } from "Vencord"; - +import { PluginProps, UserStore } from "./"; import { HexToHSL } from "./utils"; export const colorVariables: string[] = [ @@ -398,31 +396,31 @@ export function gradientBase(accentColor?: string, discordSaturation = false) { --bg-overlay-opacity-home-card: 0.9; --bg-overlay-opacity-app-frame: var(--bg-overlay-opacity-5); } -.children_cde9af:after, .form_d8a4a1:before { +.children_fc4f04:after, .form_a7d72e:before { content: none; } -.scroller_de945b { +.scroller_fea3ef { background: var(--bg-overlay-app-frame,var(--background-tertiary)); } -.expandedFolderBackground_b1385f { +.expandedFolderBackground_bc7085 { background: rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6)); } .wrapper__8436d:not(:hover):not(.selected_ae80f7) .childWrapper_a6ce15 { background: rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6)); } -.folder__17546:has(.expandedFolderIconWrapper__324c1) { +.folder_bc7085:has(.expandedFolderIconWrapper_bc7085) { background: var(--bg-overlay-6,var(--background-secondary)); } -.circleIconButton__05cf2:not(.selected_aded59) { +.circleIconButton_db6521:not(.selected_db6521) { background: rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-6)); } -.auto_a3c0bd::-webkit-scrollbar-thumb, -.thin_b1c063::-webkit-scrollbar-thumb { +.auto_eed6a8::-webkit-scrollbar-thumb, +.thin_eed6a8::-webkit-scrollbar-thumb { background-size: 200vh; background-image: -webkit-gradient(linear,left top,left bottom,from(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-4))),to(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-4)))),var(--custom-theme-background); background-image: linear-gradient(rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-4)),rgb(var(--bg-overlay-color-inverse)/var(--bg-overlay-opacity-4))),var(--custom-theme-background); } -.auto_a3c0bd::-webkit-scrollbar-track { +.auto_eed6a8::-webkit-scrollbar-track { background-size: 200vh; background-image: -webkit-gradient(linear,left top,left bottom,from(rgb(var(--bg-overlay-color)/.4)),to(rgb(var(--bg-overlay-color)/.4))),var(--custom-theme-background); background-image: linear-gradient(rgb(var(--bg-overlay-color)/.4),rgb(var(--bg-overlay-color)/.4)),var(--custom-theme-background); @@ -470,10 +468,10 @@ export function gradientBase(accentColor?: string, discordSaturation = false) { }`; } -export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean, discordSaturation: boolean, mutedTextBrightness?: number, name?: string) { +export function generateCss(primaryColor: string, secondaryColor: string, tertiaryColor: string, accentColor: string, tintedText: boolean = true, discordSaturation: boolean = true, mutedTextBrightness?: number, name?: string) { return `/** * @name ${name} - * @version ${(Plugins.plugins.DiscordColorways as any).creatorVersion} + * @version ${PluginProps.creatorVersion} * @description Automatically generated Colorway. * @author ${UserStore.getCurrentUser().username} * @authorId ${UserStore.getCurrentUser().id} @@ -529,30 +527,30 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia --primary-160-hsl: ${HexToHSL("#" + secondaryColor)[0]} calc(var(--saturation-factor, 1)*${discordSaturation ? Math.round(((HexToHSL("#" + secondaryColor)[1] / 100) * (100 + PrimarySatDiffs[660])) * 10) / 10 : HexToHSL("#" + secondaryColor)[1]}%) ${Math.min(HexToHSL("#" + secondaryColor)[2] + 76.4, 82.5)}%; --primary-200-hsl: ${HexToHSL("#" + tertiaryColor)[0]} calc(var(--saturation-factor, 1)*${HexToHSL("#" + tertiaryColor)[1]}%) ${Math.min(HexToHSL("#" + tertiaryColor)[2] + 80, 80)}%; } -.emptyPage_feb902, -.scrollerContainer_dda72c, -.container__03ec9, -.header__71942 { +.emptyPage_c6b11b, +.scrollerContainer_c6b11b, +.container_f1fd9c, +.header_f1fd9c { background-color: unset !important; } -.container__6b2e5, -.container__03ec9, -.header__71942 { +.container_c2efea, +.container_f1fd9c, +.header_f1fd9c { background: transparent !important; }${(Math.round(HexToHSL("#" + primaryColor)[2]) > 80) ? `\n\n/*Primary*/ -.theme-dark .container_bd15da, -.theme-dark .body__616e6, -.theme-dark .toolbar__62fb5, -.theme-dark .container_e1387b, -.theme-dark .messageContent_abea64, -.theme-dark .attachButtonPlus_fd0021, -.theme-dark .username__0b0e7:not([style]), -.theme-dark .children_cde9af, -.theme-dark .buttonContainer__6de7e, -.theme-dark .listItem__48528, -.theme-dark .body__616e6 .caret__33d19, -.theme-dark .body__616e6 .titleWrapper_d6133e > h1, -.theme-dark .body__616e6 .icon_ae0b42 { +.theme-dark .container_c2739c, +.theme-dark .body_cd82a7, +.theme-dark .toolbar_fc4f04, +.theme-dark .container_f0fccd, +.theme-dark .messageContent_f9f2ca, +.theme-dark .attachButtonPlus_f298d4, +.theme-dark .username_f9f2ca:not([style]), +.theme-dark .children_fc4f04, +.theme-dark .buttonContainer_f9f2ca, +.theme-dark .listItem_c96c45, +.theme-dark .body_cd82a7 .caret_fc4f04, +.theme-dark .body_cd82a7 .titleWrapper_fc4f04 > h1, +.theme-dark .body_cd82a7 .icon_fc4f04 { --white-500: black !important; --interactive-normal: black !important; --text-normal: black !important; @@ -561,23 +559,23 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia --header-secondary: black !important; } -.theme-dark .contentRegionScroller__9ae20 :not(.mtk1,.mtk2,.mtk3,.mtk4,.mtk5,.mtk6,.mtk7,.mtk8,.mtk9,.monaco-editor .line-numbers) { +.theme-dark .contentRegionScroller_c25c6d :not(.mtk1,.mtk2,.mtk3,.mtk4,.mtk5,.mtk6,.mtk7,.mtk8,.mtk9,.monaco-editor .line-numbers) { --white-500: black !important; } -.theme-dark .container__26baa { +.theme-dark .container_fc4f04 { --channel-icon: black; } -.theme-dark .callContainer__1477d { +.theme-dark .callContainer_d880dc { --white-500: ${(HexToHSL("#" + tertiaryColor)[2] > 80) ? "black" : "white"} !important; } -.theme-dark .channelTextArea_c2094b { +.theme-dark .channelTextArea_a7d72e { --text-normal: ${(HexToHSL("#" + primaryColor)[2] + 3.6 > 80) ? "black" : "white"}; } -.theme-dark .placeholder_dec8c7 { +.theme-dark .placeholder_a552a6 { --channel-text-area-placeholder: ${(HexToHSL("#" + primaryColor)[2] + 3.6 > 80) ? "black" : "white"}; opacity: .6; } @@ -586,16 +584,16 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia background-color: black; } -.theme-dark .root_a28985 > .header__5e5a6 > h1 { +.theme-dark .root_f9a4c9 > .header_f9a4c9 > h1 { color: black; } /*End Primary*/`: ""}${(HexToHSL("#" + secondaryColor)[2] > 80) ? `\n\n/*Secondary*/ -.theme-dark .wrapper__3c6d5 *, -.theme-dark .sidebar_e031be *:not(.hasBanner__04337 *), -.theme-dark .members__573eb *:not([style]), -.theme-dark .sidebarRegionScroller__8113e *, -.theme-dark .header__8e271, -.theme-dark .lookFilled__950dd.colorPrimary_ebe632 { +.theme-dark .wrapper_cd82a7 *, +.theme-dark .sidebar_a4d4d9 *:not(.hasBanner_fd6364 *), +.theme-dark .members_cbd271 *:not([style]), +.theme-dark .sidebarRegionScroller_c25c6d *, +.theme-dark .header_e06857, +.theme-dark .lookFilled_dd4f85.colorPrimary_dd4f85 { --white-500: black !important; --channels-default: black !important; --channel-icon: black !important; @@ -604,36 +602,36 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia --interactive-active: var(--white-500); } -.theme-dark .channelRow__538ef { +.theme-dark .channelRow_f04d06 { background-color: var(--background-secondary); } -.theme-dark .channelRow__538ef * { +.theme-dark .channelRow_f04d06 * { --channel-icon: black; } -.theme-dark #app-mount .activity_bafb94 { +.theme-dark #app-mount .activity_a31c43 { --channels-default: var(--white-500) !important; } -.theme-dark .nameTag__77ab2 { +.theme-dark .nameTag_b2ca13 { --header-primary: black !important; --header-secondary: ${HexToHSL("#" + secondaryColor)[0] === 0 ? "gray" : ((HexToHSL("#" + secondaryColor)[2] < 80) ? "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 90%)" : "hsl(" + HexToHSL("#" + secondaryColor)[0] + ", calc(var(--saturation-factor, 1)*100%), 20%)")} !important; } -.theme-dark .bannerVisible_ef30fe .headerContent__6fcc7 { +.theme-dark .bannerVisible_fd6364 .headerContent_fd6364 { color: #fff; } -.theme-dark .embedFull__14919 { +.theme-dark .embedFull_b0068a { --text-normal: black; } /*End Secondary*/`: ""}${HexToHSL("#" + tertiaryColor)[2] > 80 ? `\n\n/*Tertiary*/ -.theme-dark .winButton_f17fb6, -.theme-dark .searchBar__310d8 *, -.theme-dark .wordmarkWindows_ffbc5e, -.theme-dark .searchBar__5a20a *, -.theme-dark .searchBarComponent__8f95f { +.theme-dark .winButton_a934d8, +.theme-dark .searchBar_e0840f *, +.theme-dark .wordmarkWindows_a934d8, +.theme-dark .searchBar_a46bef *, +.theme-dark .searchBarComponent_f0963d { --white-500: black !important; } @@ -641,25 +639,25 @@ export function generateCss(primaryColor: string, secondaryColor: string, tertia color: ${HexToHSL("#" + secondaryColor)[2] > 80 ? "black" : "white"}; } -.theme-dark .popout__24e32 > * { +.theme-dark .popout_c5b389 > * { --interactive-normal: black !important; --header-secondary: black !important; } -.theme-dark .tooltip__7b090 { +.theme-dark .tooltip_b6c360 { --text-normal: black !important; } -.theme-dark .children_cde9af .icon_ae0b42 { +.theme-dark .children_fc4f04 .icon_fc4f04 { color: var(--interactive-active) !important; } /*End Tertiary*/`: ""}${HexToHSL("#" + accentColor)[2] > 80 ? `\n\n/*Accent*/ -.selected_aded59 *, +.selected_db6521 *, .selected_ae80f7 *, -#app-mount .lookFilled__950dd.colorBrand__27d57:not(.buttonColor__7bad9), -.colorDefault_e361cf.focused_dcafb9, -.row__9e25f:hover, +#app-mount .lookFilled_dd4f85.colorBrand_dd4f85:not(.buttonColor_adcaac), +.colorDefault_d90b3d.focused_d90b3d, +.row_c5b389:hover, .colorwayInfoIcon, -.checkmarkCircle_b1b1cc > circle { +.checkmarkCircle_cb7c27 > circle { --white-500: black !important; } @@ -789,10 +787,10 @@ export function getAutoPresets(accentColor?: string) { --primary-400: hsl(${HexToHSL("#" + accentColor)[0]}, calc(var(--saturation-factor, 1)*12%), 90%); --primary-360: hsl(${HexToHSL("#" + accentColor)[0]}, calc(var(--saturation-factor, 1)*12%), 90%); } -.emptyPage_feb902, -.scrollerContainer_dda72c, -.container__03ec9, -.header__71942 { +.emptyPage_c6b11b, +.scrollerContainer_c6b11b, +.container_f1fd9c, +.header_f1fd9c { background-color: unset !important; }`; } @@ -816,7 +814,25 @@ export function getAutoPresets(accentColor?: string) { } as { [key: string]: { name: string, id: string, preset: () => string; }; }; } -export function getPreset(primaryColor?: string, secondaryColor?: string, tertiaryColor?: string, accentColor?: string): { [preset: string]: { name: string, preset: (...args: any) => string | { full: string, base: string; }, id: string, colors: string[]; }; } { +export function getPreset( + primaryColor?: string, + secondaryColor?: string, + tertiaryColor?: string, + accentColor?: string +): { + [preset: string]: { + name: string, + preset: (...args: any) => string | { full: string, base: string; }, + id: string, + colors: string[], + calculated?: { + accent?: string, + primary?: string, + secondary?: string, + tertiary?: string; + }; + }; +} { function cyanLegacy(discordSaturation = false) { return `:root:root { --cyan-accent-color: #${accentColor}; @@ -979,7 +995,12 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia name: "Hue Rotation", preset: getAutoPresets(accentColor).hueRotation.preset, id: "hueRotation", - colors: ["accent"] + colors: ["accent"], + calculated: { + primary: `hsl(${HexToHSL("#" + accentColor)[0]} 11% 21%)`, + secondary: `hsl(${HexToHSL("#" + accentColor)[0]} 11% 18%)`, + tertiary: `hsl(${HexToHSL("#" + accentColor)[0]} 10% 13%)` + } }, accentSwap: { name: "Accent Swap", @@ -991,7 +1012,12 @@ export function getPreset(primaryColor?: string, secondaryColor?: string, tertia name: "Material You", preset: getAutoPresets(accentColor).materialYou.preset, id: "materialYou", - colors: ["accent"] + colors: ["accent"], + calculated: { + primary: `hsl(${HexToHSL("#" + accentColor)[0]} 12% 12%)`, + secondary: `hsl(${HexToHSL("#" + accentColor)[0]} 12% 16%)`, + tertiary: `hsl(${HexToHSL("#" + accentColor)[0]} 16% 18%)` + } } }; } diff --git a/src/equicordplugins/discordColorways/defaultsLoader.tsx b/src/equicordplugins/discordColorways/defaultsLoader.tsx new file mode 100644 index 00000000..5dce8dd6 --- /dev/null +++ b/src/equicordplugins/discordColorways/defaultsLoader.tsx @@ -0,0 +1,117 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "."; +import { defaultColorwaySource, nullColorwayObj } from "./constants"; + +export default async function () { + const [ + customColorways, + colorwaySourceFiles, + showColorwaysButton, + onDemandWays, + onDemandWaysTintedText, + onDemandWaysDiscordSaturation, + onDemandWaysOsAccentColor, + activeColorwayObject, + colorwaysPluginTheme, + colorwaysBoundManagers, + colorwaysManagerAutoconnectPeriod, + colorwaysManagerDoAutoconnect + ] = await DataStore.getMany([ + "customColorways", + "colorwaySourceFiles", + "showColorwaysButton", + "onDemandWays", + "onDemandWaysTintedText", + "onDemandWaysDiscordSaturation", + "onDemandWaysOsAccentColor", + "activeColorwayObject", + "colorwaysPluginTheme", + "colorwaysBoundManagers", + "colorwaysManagerAutoconnectPeriod", + "colorwaysManagerDoAutoconnect" + ]); + + const defaults = [ + { + name: "colorwaysManagerAutoconnectPeriod", + value: colorwaysManagerAutoconnectPeriod, + default: 3000 + }, + { + name: "colorwaysManagerDoAutoconnect", + value: colorwaysManagerDoAutoconnect, + default: true + }, + { + name: "showColorwaysButton", + value: showColorwaysButton, + default: false + }, + { + name: "onDemandWays", + value: onDemandWays, + default: false + }, + { + name: "onDemandWaysTintedText", + value: onDemandWaysTintedText, + default: true + }, + { + name: "onDemandWaysDiscordSaturation", + value: onDemandWaysDiscordSaturation, + default: false + }, + { + name: "onDemandWaysOsAccentColor", + value: onDemandWaysOsAccentColor, + default: false + }, + { + name: "colorwaysBoundManagers", + value: colorwaysBoundManagers, + default: [] + }, + { + name: "activeColorwayObject", + value: activeColorwayObject, + default: nullColorwayObj + }, + { + name: "colorwaysPluginTheme", + value: colorwaysPluginTheme, + default: "discord" + } + ]; + + defaults.forEach(({ name, value, default: def }) => { + if (!value) DataStore.set(name, def); + }); + + if (customColorways) { + if (!customColorways[0].colorways) { + DataStore.set("customColorways", [{ name: "Custom", colorways: customColorways }]); + } + } else { + DataStore.set("customColorways", []); + } + + if (colorwaySourceFiles) { + if (typeof colorwaySourceFiles[0] === "string") { + DataStore.set("colorwaySourceFiles", colorwaySourceFiles.map((sourceURL: string, i: number) => { + return { name: sourceURL === defaultColorwaySource ? "Project Colorway" : `Source #${i}`, url: sourceURL === "https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json" ? defaultColorwaySource : sourceURL }; + })); + } + } else { + DataStore.set("colorwaySourceFiles", [{ + name: "Project Colorway", + url: defaultColorwaySource + }]); + } + +} diff --git a/src/equicordplugins/discordColorways/index.tsx b/src/equicordplugins/discordColorways/index.tsx index 25bda89d..10ab8051 100644 --- a/src/equicordplugins/discordColorways/index.tsx +++ b/src/equicordplugins/discordColorways/index.tsx @@ -4,27 +4,30 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import * as DataStore from "@api/DataStore"; +// Plugin Imports +import * as $DataStore from "@api/DataStore"; import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { disableStyle, enableStyle } from "@api/Styles"; -import { Flex } from "@components/Flex"; import { Devs, EquicordDevs } from "@utils/constants"; -import { ModalProps, openModal } from "@utils/modal"; +import { openModal } from "@utils/modal"; import definePlugin from "@utils/types"; import { - Button, - Clipboard, - Forms, i18n, - SettingsRouter, - Toasts + SettingsRouter } from "@webpack/common"; +import { FluxEvents as $FluxEvents } from "@webpack/types"; +// Mod-specific imports +import { + CSSProperties as $CSSProperties, + ReactNode as $ReactNode +} from "react"; -import AutoColorwaySelector from "./components/AutoColorwaySelector"; -import ColorPickerModal from "./components/ColorPicker"; +import { ColorwayCSS } from "./colorwaysAPI"; +import ColorwayID from "./components/ColorwayID"; import ColorwaysButton from "./components/ColorwaysButton"; import CreatorModal from "./components/CreatorModal"; +import PCSMigrationModal from "./components/PCSMigrationModal"; import Selector from "./components/Selector"; import OnDemandWaysPage from "./components/SettingsTabs/OnDemandPage"; import SettingsPage from "./components/SettingsTabs/SettingsPage"; @@ -32,97 +35,45 @@ import SourceManager from "./components/SettingsTabs/SourceManager"; import Store from "./components/SettingsTabs/Store"; import Spinner from "./components/Spinner"; import { defaultColorwaySource } from "./constants"; -import { generateCss, getAutoPresets } from "./css"; +import defaultsLoader from "./defaultsLoader"; import style from "./style.css?managed"; +import discordTheme from "./theme.discord.css?managed"; import { ColorPickerProps, ColorwayObject } from "./types"; -import { colorToHex, hexToString } from "./utils"; +import { connect } from "./wsClient"; + +export const DataStore = $DataStore; +export type ReactNode = $ReactNode; +export type CSSProperties = $CSSProperties; +export type FluxEvents = $FluxEvents; +export { closeModal, openModal } from "@utils/modal"; +export { + Clipboard, + FluxDispatcher, + i18n, + ReactDOM, + SettingsRouter, + Slider, + Toasts, + useCallback, + useEffect, + useReducer, + useRef, + UserStore, + useState, + useStateFromStores +} from "@webpack/common"; export let ColorPicker: React.FunctionComponent = () => { return ; }; -(async function () { - const [ - customColorways, - colorwaySourceFiles, - showColorwaysButton, - onDemandWays, - onDemandWaysTintedText, - useThinMenuButton, - onDemandWaysDiscordSaturation, - onDemandWaysOsAccentColor, - activeColorwayObject, - selectorViewMode, - showLabelsInSelectorGridView - ] = await DataStore.getMany([ - "customColorways", - "colorwaySourceFiles", - "showColorwaysButton", - "onDemandWays", - "onDemandWaysTintedText", - "useThinMenuButton", - "onDemandWaysDiscordSaturation", - "onDemandWaysOsAccentColor", - "activeColorwayObject", - "selectorViewMode", - "showLabelsInSelectorGridView" - ]); +defaultsLoader(); - const defaults = [ - { name: "showColorwaysButton", value: showColorwaysButton, default: false }, - { name: "onDemandWays", value: onDemandWays, default: false }, - { name: "onDemandWaysTintedText", value: onDemandWaysTintedText, default: true }, - { name: "useThinMenuButton", value: useThinMenuButton, default: false }, - { name: "onDemandWaysDiscordSaturation", value: onDemandWaysDiscordSaturation, default: false }, - { name: "onDemandWaysOsAccentColor", value: onDemandWaysOsAccentColor, default: false }, - { name: "activeColorwayObject", value: activeColorwayObject, default: { id: null, css: null, sourceType: null, source: null } }, - { name: "selectorViewMode", value: selectorViewMode, default: "grid" }, - { name: "showLabelsInSelectorGridView", value: showLabelsInSelectorGridView, default: false } - ]; - - defaults.forEach(({ name, value, default: def }) => { - if (!value) DataStore.set(name, def); - }); - - if (customColorways) { - if (!customColorways[0]?.colorways) { - DataStore.set("customColorways", [{ name: "Custom", colorways: customColorways }]); - } - } else { - DataStore.set("customColorways", []); - } - - if (colorwaySourceFiles) { - if (typeof colorwaySourceFiles[0] === "string") { - DataStore.set("colorwaySourceFiles", colorwaySourceFiles.map((sourceURL: string, i: number) => { - return { name: sourceURL === defaultColorwaySource ? "Project Colorway" : `Source #${i}`, url: sourceURL }; - })); - } - } else { - DataStore.set("colorwaySourceFiles", [{ - name: "Project Colorway", - url: defaultColorwaySource - }]); - } - -})(); - -export const ColorwayCSS = { - get: () => document.getElementById("activeColorwayCSS")!.textContent || "", - set: (e: string) => { - if (!document.getElementById("activeColorwayCSS")) { - document.head.append(Object.assign(document.createElement("style"), { - id: "activeColorwayCSS", - textContent: e - })); - } else document.getElementById("activeColorwayCSS")!.textContent = e; - }, - remove: () => document.getElementById("activeColorwayCSS")!.remove(), -}; - -export const versionData = { - pluginVersion: "5.7.1", - creatorVersion: "1.20", +export const PluginProps = { + pluginVersion: "6.1.0", + clientMod: "Vencord User Plugin", + UIVersion: "2.0.0", + creatorVersion: "1.20" }; export default definePlugin({ @@ -130,38 +81,17 @@ export default definePlugin({ description: "A plugin that offers easy access to simple color schemes/themes for Discord, also known as Colorways", authors: [EquicordDevs.DaBluLite, Devs.ImLvna], dependencies: ["ServerListAPI", "MessageAccessoriesAPI"], - pluginVersion: versionData.pluginVersion, - creatorVersion: versionData.creatorVersion, + pluginVersion: PluginProps.pluginVersion, toolboxActions: { - "Change Colorway": () => openModal(props => ), "Open Colorway Creator": () => openModal(props => ), - "Open Color Stealer": () => openModal(props => ), "Open Settings": () => SettingsRouter.open("ColorwaysSettings"), - "Open On-Demand Settings": () => SettingsRouter.open("ColorwaysOnDemand"), - "Manage Colorways...": () => SettingsRouter.open("ColorwaysManagement"), - "Change Auto Colorway Preset": async () => { - const [ - activeAutoPreset, - activeColorwayObject - ] = await DataStore.getMany([ - "activeAutoPreset", - "activeColorwayObject" - ]); - openModal((props: ModalProps) => { - if (activeColorwayObject.id === "Auto") { - const demandedColorway = getAutoPresets(colorToHex(getComputedStyle(document.body).getPropertyValue("--os-accent-color")))[autoPresetId].preset(); - DataStore.set("activeColorwayObject", { id: "Auto", css: demandedColorway, sourceType: "online", source: null }); - ColorwayCSS.set(demandedColorway); - } - }} />); - } }, patches: [ // Credits to Kyuuhachi for the BetterSettings plugin patches { find: "this.renderArtisanalHack()", replacement: { - match: /createPromise:\(\)=>([^:}]*?),webpackId:"?\d+"?,name:(?!="CollectiblesShop")"[^"]+"/g, + match: /createPromise:\(\)=>([^:}]*?),webpackId:"\d+",name:(?!="CollectiblesShop")"[^"]+"/g, replace: "$&,_:$1", predicate: () => true } @@ -170,8 +100,8 @@ export default definePlugin({ { find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format", replacement: { - match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, - replace: "$&(async ()=>$2)()," + match: /(?<=(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,)(?=\1\(this)/, + replace: "(async ()=>$2)()," }, predicate: () => true }, @@ -208,6 +138,57 @@ export default definePlugin({ patchedSettings: new WeakSet(), + addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record) { + if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; + + this.patchedSettings.add(elements); + + elements.push(...this.makeSettingsCategories(sectionTypes)); + }, + + makeSettingsCategories(SectionTypes: Record) { + return [ + { + section: SectionTypes.HEADER, + label: "Discord Colorways", + className: "vc-settings-header" + }, + { + section: "ColorwaysSelector", + label: "Colorways", + element: () => , + className: "dc-colorway-selector" + }, + { + section: "ColorwaysSettings", + label: "Settings", + element: () => , + className: "dc-colorway-settings" + }, + { + section: "ColorwaysSourceManager", + label: "Sources", + element: () => , + className: "dc-colorway-sources-manager" + }, + { + section: "ColorwaysOnDemand", + label: "On-Demand", + element: () => , + className: "dc-colorway-ondemand" + }, + { + section: "ColorwaysStore", + label: "Store", + element: () => , + className: "dc-colorway-store" + }, + { + section: SectionTypes.DIVIDER + } + ].filter(Boolean); + }, + ColorwaysButton: () => , async start() { @@ -220,31 +201,31 @@ export default definePlugin({ const ColorwaysSelector = () => ({ section: "ColorwaysSelector", label: "Colorways Selector", - element: () => new Promise(() => true), transitionState: 1 }} />, + element: () => , className: "dc-colorway-selector" }); const ColorwaysSettings = () => ({ section: "ColorwaysSettings", label: "Colorways Settings", - element: SettingsPage, + element: () => , className: "dc-colorway-settings" }); const ColorwaysSourceManager = () => ({ section: "ColorwaysSourceManager", label: "Colorways Sources", - element: SourceManager, + element: () => , className: "dc-colorway-sources-manager" }); const ColorwaysOnDemand = () => ({ section: "ColorwaysOnDemand", label: "Colorways On-Demand", - element: OnDemandWaysPage, + element: () => , className: "dc-colorway-ondemand" }); const ColorwaysStore = () => ({ section: "ColorwaysStore", label: "Colorways Store", - element: Store, + element: () => , className: "dc-colorway-store" }); @@ -252,109 +233,25 @@ export default definePlugin({ addServerListElement(ServerListRenderPosition.Above, this.ColorwaysButton); + connect(); + enableStyle(style); + enableStyle(discordTheme); ColorwayCSS.set((await DataStore.get("activeColorwayObject") as ColorwayObject).css || ""); - addAccessory("colorways-btn", props => { - if (String(props.message.content).match(/colorway:[0-9a-f]{0,100}/)) { - return - {String(props.message.content).match(/colorway:[0-9a-f]{0,100}/g)?.map((colorID: string) => { - colorID = hexToString(colorID.split("colorway:")[1]); - return
-
- {(() => { - if (colorID) { - if (!colorID.includes(",")) { - throw new Error("Invalid Colorway ID"); - } else { - return colorID.split("|").filter(string => string.includes(",#"))[0].split(/,#/).map((color: string) =>
); - } - } else return null; - })()} -
-
- Colorway{/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID) ? `: ${/n:([A-Za-z0-9]+( [A-Za-z0-9]+)+)/i.exec(colorID)![1]}` : ""} - - - - - -
-
; - })} - ; - } else { - return null; - } - }); + if ((await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json") || (!(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/DaBluLite/ProjectColorway/master/index.json") && !(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).map(i => i.url).includes("https://raw.githubusercontent.com/ProjectColorway/ProjectColorway/master/index.json"))) { + DataStore.set("colorwaySourceFiles", [{ name: "Project Colorway", url: defaultColorwaySource }, ...(await DataStore.get("colorwaySourceFiles") as { name: string, url: string; }[]).filter(i => i.name !== "Project Colorway")]); + openModal(props => ); + } + + addAccessory("colorway-id-card", props => ); }, stop() { - removeServerListElement(ServerListRenderPosition.In, this.ColorwaysButton); + removeServerListElement(ServerListRenderPosition.Above, this.ColorwaysButton); disableStyle(style); + disableStyle(discordTheme); ColorwayCSS.remove(); - removeAccessory("colorways-btn"); + removeAccessory("colorway-id-card"); const customSettingsSections = ( Vencord.Plugins.plugins.Settings as any as { customSections: ((ID: Record) => any)[]; diff --git a/src/equicordplugins/discordColorways/style.css b/src/equicordplugins/discordColorways/style.css index d12bf5aa..e44ba331 100644 --- a/src/equicordplugins/discordColorways/style.css +++ b/src/equicordplugins/discordColorways/style.css @@ -1,3 +1,4 @@ +/* stylelint-disable color-function-notation */ /* stylelint-disable no-descending-specificity */ /* stylelint-disable declaration-block-no-redundant-longhand-properties */ /* stylelint-disable selector-id-pattern */ @@ -11,7 +12,7 @@ display: flex; justify-content: center; align-items: center; - transition: .15s ease-out; + transition: 0.15s ease-out; background-color: var(--background-primary); cursor: pointer; color: var(--text-normal); @@ -22,20 +23,6 @@ border-radius: 16px; } -.discordColorway { - width: 56px; - cursor: pointer; - display: flex; - flex-direction: column; - position: relative; - align-items: center; - transition: 170ms ease; -} - -.discordColorway:hover { - filter: brightness(.8); -} - .discordColorwayPreviewColorContainer { display: flex; flex-flow: wrap; @@ -53,43 +40,261 @@ height: 50%; } -.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(2)))>.discordColorwayPreviewColor { +.discordColorwayPreviewColorContainer:not(:has(> .discordColorwayPreviewColor:nth-child(2)))>.discordColorwayPreviewColor { height: 100%; width: 100%; } -.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor { +.discordColorwayPreviewColorContainer:not(:has(> .discordColorwayPreviewColor:nth-child(3)))>.discordColorwayPreviewColor { height: 100%; } -.discordColorwayPreviewColorContainer:not(:has(>.discordColorwayPreviewColor:nth-child(4)))>.discordColorwayPreviewColor:nth-child(3) { +.discordColorwayPreviewColorContainer:not(:has(> .discordColorwayPreviewColor:nth-child(4)))>.discordColorwayPreviewColor:nth-child(3) { width: 100%; } .ColorwaySelectorWrapper { position: relative; display: flex; - gap: 16px 28px; + gap: 8px; width: 100%; - flex-wrap: wrap; - padding: 2px; scrollbar-width: none !important; + flex-direction: column; + padding: 0 16px !important; + box-sizing: border-box; + overflow: hidden auto; } .ColorwaySelectorWrapper::-webkit-scrollbar { width: 0; } -.colorwaySelectorModal { - width: 100% !important; - min-width: 596px !important; +.colorwaySelectorModal, +.colorwayModal { + width: 90% !important; + height: 90% !important; + border-radius: 8px; + border: 1px solid #a6a6a6f0; + display: flex; + flex-direction: row; + background-color: #090909; + margin: 0 auto; + pointer-events: all; + position: relative; + animation: show-modal 0.2s ease-in-out; +} + +@keyframes reveal-modal { + from { + translate: 0 -20px; + } + + to { + translate: 0; + } +} + +@keyframes reveal-modal-backdrop { + from { + opacity: 0; + } + + to { + opacity: 0.75; + } +} + +.colorwaysModalBackdrop { + background-color: #707070; + opacity: 0.75; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1; + transition: 0.4s ease; + animation: reveal-modal-backdrop 0.4s ease; + pointer-events: all; +} + +.colorwayModal { + flex-direction: column; +} + +.colorwaySelectorModal.closing, +.colorwayModal.closing, +.colorwaysPreview-modal.closing, +.colorwaysModal.closing { + animation: close-modal 0.2s ease-in-out; + transform: scale(0.5); + opacity: 0; +} + +.colorwaySelectorModal.hidden, +.colorwayModal.hidden, +.colorwaysPreview-modal.hidden, +.colorwaysModal.hidden { + animation: close-modal 0.2s ease-in-out; + transform: scale(0.5); + opacity: 0; +} + +@keyframes show-modal { + 0% { + transform: scale(0.7); + opacity: 0; + } + + 75% { + transform: scale(1.009); + opacity: 1; + } + + 100% { + transform: scale(1); + opacity: 1; + } +} + +@keyframes close-modal { + from { + transform: scale(1); + opacity: 1; + } + + to { + transform: scale(0.7); + opacity: 0; + } +} + +.colorwaysSettingsDivider { + width: 100%; + height: 1px; + border-top: thin solid #fff; + margin-top: 20px; +} + +.colorwaysSettings-switch { + background-color: rgb(85, 87, 94); + flex: 0 0 auto; + position: relative; + border-radius: 14px; + width: 40px; + height: 24px; + transition: 0.15s ease; + cursor: pointer; +} + +.colorwaysSettings-switch.checked { + background-color: #fff; +} + +.colorwaySwitch-label { + flex: 1; + display: block; + overflow: hidden; + margin-top: 0; + margin-bottom: 0; + color: #fff; + line-height: 24px; + font-size: 16px; + font-weight: 500; + word-wrap: break-word; + cursor: pointer; +} + +.colorwaysNote { + color: #fff; + opacity: 0.5; +} + +.colorwayModal-selectorHeader { + display: flex; + width: 100%; + padding: 16px; + padding-bottom: 8px; + box-sizing: border-box; + flex-direction: column; + gap: 8px; +} + +.colorwayModalContent { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; +} + +.colorwaySelectorSidebar-tab { + width: 48px; + height: 48px; + border-radius: 8px; + cursor: pointer; + transition: 0.2s ease; + border: 1px solid transparent; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; +} + +.colorwaysPillButton { + padding: 4px 12px; + border-radius: 16px; + background-color: #101010; + color: #fff; + transition: 0.2s ease; + cursor: pointer; + display: flex; + gap: 0.5rem; + justify-content: center; + align-items: center; + width: fit-content; + height: 32px; +} + +.colorwaysPillButton.colorwaysPillButton-onSurface { + background-color: #1a1a1a; +} + +.colorwaysPillButton.colorwaysPillButton-icon { + padding: 4px; +} + +.colorwaysPillButton:hover { + background-color: #2a2a2a; +} + +.colorwaySelectorSidebar-tab:hover { + background-color: #2a2a2a; +} + +.colorwaySelectorSidebar-tab.active { + background-color: #0a0a0a; + border-color: #a6a6a6; +} + +.colorwaySelectorSidebar { + background-color: #101010; + color: #fff; + box-sizing: border-box; + height: 100%; + flex: 0 0 auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 8px; + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; } .colorwaySelectorModalContent { display: flex; flex-direction: column; width: 100%; - max-width: 596px; + height: 100%; overflow: visible !important; padding: 0 16px !important; } @@ -125,7 +330,7 @@ .discordColorway:hover .colorwayInfoIconContainer { opacity: 1; - transition: .15s; + transition: 0.15s; } .colorwayCreator-swatch { @@ -230,16 +435,16 @@ .colorwayInfo-colorSwatch { display: flex; - width: 100%; + width: 100px; height: 38px; border-radius: 3px; cursor: pointer; position: relative; - transition: .15s; + transition: 0.15s; } .colorwayInfo-colorSwatch:hover { - filter: brightness(.8); + filter: brightness(0.8); } .colorwayInfo-row { @@ -304,10 +509,9 @@ display: flex; flex-direction: column; padding: 10px; - border-radius: 4px; - background-color: var(--background-secondary); + border-radius: 8px; + background-color: #1a1a1a; box-sizing: border-box; - color: var(--header-secondary); max-height: 250px; overflow: hidden overlay; } @@ -344,11 +548,12 @@ } .colorwaysCreator-settingsList { - overflow: auto; + overflow: hidden auto; + scrollbar-width: none !important; max-height: 185px; } -.colorwaysCreator-settingCat-collapsed>:is(.colorwaysCreator-settingsList, .colorwayInfo-cssCodeblock), +.colorwaysCreator-settingCat-collapsed> :is(.colorwaysCreator-settingsList, .colorwayInfo-cssCodeblock), .colorwaysColorpicker-collapsed { display: none !important; } @@ -386,6 +591,11 @@ max-height: unset !important; width: fit-content; height: fit-content; + pointer-events: all; +} + +.colorwaysPreview-modal>.colorwaysPreview-wrapper { + height: 100%; } .colorwaysPreview-titlebar { @@ -476,7 +686,7 @@ width: 48px; height: 48px; border-radius: 50px; - transition: .2s ease; + transition: 0.2s ease; display: flex; justify-content: center; align-items: center; @@ -489,7 +699,7 @@ .colorwayPreview-guildSeparator { width: 32px; height: 2px; - opacity: .48; + opacity: 0.48; border-radius: 1px; } @@ -518,7 +728,7 @@ display: flex; justify-content: center; align-items: center; - transition: .15s ease; + transition: 0.15s ease; cursor: pointer; color: var(--interactive-normal); } @@ -567,7 +777,10 @@ } .colorwayPreview-topShadow { - box-shadow: 0 1px 0 hsl(var(--primary-900-hsl)/20%), 0 1.5px 0 hsl(var(--primary-860-hsl)/5%), 0 2px 0 hsl(var(--primary-900-hsl)/5%); + box-shadow: + 0 1px 0 hsl(var(--primary-900-hsl) / 20%), + 0 1.5px 0 hsl(var(--primary-860-hsl) / 5%), + 0 2px 0 hsl(var(--primary-900-hsl) / 5%); width: 100%; height: 32px; font-family: var(--font-display); @@ -584,7 +797,7 @@ } .colorwayPreview-channels>.colorwayPreview-topShadow:hover { - background-color: hsl(var(--primary-500-hsl)/30%); + background-color: hsl(var(--primary-500-hsl) / 30%); } .colorwaysPreview-wrapper:fullscreen .colorwayPreview-topShadow { @@ -608,7 +821,9 @@ box-sizing: border-box; width: 100%; height: 100%; - transition: transform .1s ease-out, opacity .1s ease-out; + transition: + transform 0.1s ease-out, + opacity 0.1s ease-out; color: var(--interactive-normal); } @@ -621,11 +836,27 @@ .colorwaySelector-search { width: 100%; + border-radius: 6px; + background-color: #101010; + transition: 0.2s ease; + border: 1px solid transparent; + padding-left: 12px; + color: #fff; + height: 40px; + box-sizing: border-box; +} + +.colorwaySelector-search:hover { + background-color: #1a1a1a; +} + +.colorwaySelector-search:focus { + background-color: #1a1a1a; + border-color: #a6a6a6; } .colorwaySelector-sources { flex: 0 0 auto; - margin-right: auto; color: var(--button-outline-primary-text); border-color: var(--button-outline-primary-border); } @@ -720,15 +951,15 @@ margin-top: -4px; margin-left: -3px; border-radius: 50%; - opacity: .3; + opacity: 0.3; } .theme-dark .colorwaysChangelog-li::before { - background-color: hsl(216deg calc(var(--saturation-factor, 1)*9.8%) 90%); + background-color: hsl(216deg calc(var(--saturation-factor, 1) * 9.8%) 90%); } .theme-light .colorwaysChangelog-li::before { - background-color: hsl(223deg calc(var(--saturation-factor, 1)*5.8%) 52.9%); + background-color: hsl(223deg calc(var(--saturation-factor, 1) * 5.8%) 52.9%); } .ColorwaySelectorWrapper .colorwayToolbox-list { @@ -739,7 +970,7 @@ border-radius: 20px; box-sizing: border-box; color: var(--text-normal); - transition: .15s ease; + transition: 0.15s ease; width: 100%; margin-left: 0; height: fit-content; @@ -767,7 +998,7 @@ height: 1px; flex: 1 1 auto; margin-left: 4px; - opacity: .6; + opacity: 0.6; background-color: currentcolor; } @@ -776,7 +1007,7 @@ } .colorwaysSelector-changelogHeader_fixed { - color: hsl(359deg calc(var(--saturation-factor, 1)*87.3%) 59.8%); + color: hsl(359deg calc(var(--saturation-factor, 1) * 87.3%) 59.8%); } .colorwaysSelector-changelogHeader_changed { @@ -901,13 +1132,17 @@ .colorwaysBtn-spinnerBeam2 { stroke: currentcolor; opacity: 0.6; - animation-delay: .15s; + animation-delay: 0.15s; } .colorwaysBtn-spinnerBeam3 { stroke: currentcolor; opacity: 0.3; - animation-delay: .23s; + animation-delay: 0.23s; +} + +.colorwaysModalTab { + padding: 16px; } .colorwaysSettings-colorwaySource { @@ -916,21 +1151,36 @@ justify-content: space-between; padding: 8px; gap: 5px; - border-radius: 4px; + border-radius: 8px; box-sizing: border-box; align-items: center; + background-color: #101010; } -.discordColorway-listItem { +.discordColorway { display: flex; flex-direction: row; justify-content: start; padding: 0 8px; gap: 5px; - border-radius: 4px; + border-radius: 6px; + background-color: #101010; box-sizing: border-box; min-height: 44px; align-items: center; + border: 1px solid transparent; + transition: 0.2s ease; + cursor: pointer; +} + +.discordColorway:hover { + background-color: #2a2a2a; + filter: brightness(0.8); +} + +.discordColorway[aria-checked="true"] { + background-color: #2a2a2a; + border-color: #a6a6a6; } .colorwaysSettings-modalRoot { @@ -954,6 +1204,13 @@ font-size: 16px; } +.colorwaysModalSectionHeader, +.colorwaysSettings-colorwaySourceLabel, +.colorwaysSettings-colorwaySourceLabelHeader, +.colorwaysSettings-colorwaySourceDesc { + color: #fff; +} + .colorwaysSettings-colorwaySourceDesc { overflow: hidden; text-overflow: ellipsis; @@ -1020,7 +1277,7 @@ background: var(--brand-500); width: 0; animation: loading-bar 2s linear infinite; - transition: .2s ease; + transition: 0.2s ease; } .colorwaysSettingsSelector-wrapper { @@ -1034,7 +1291,7 @@ border-radius: 50px; padding: 12px 16px; background-color: var(--background-tertiary); - transition: .15s ease; + transition: 0.15s ease; border: 1px solid transparent; color: var(--interactive-normal); } @@ -1064,6 +1321,7 @@ width: 100%; text-align: center; justify-content: center; + color: #fff; } .ColorwaySelectorBtn_thin { @@ -1080,19 +1338,19 @@ } .colorways-badge { - font-size: .625rem; + font-size: 0.625rem; text-transform: uppercase; vertical-align: top; display: inline-flex; align-items: center; text-indent: 0; - background: var(--brand-experiment); - color: var(--white-500); + background: #fff; + color: #000; flex: 0 0 auto; height: 15px; padding: 0 4px; margin-top: 7.5px; - border-radius: 4px; + border-radius: 16px; } .hoverRoll { @@ -1135,7 +1393,7 @@ text-overflow: ellipsis; overflow: hidden; display: block; - transition: all .22s ease; + transition: all 0.22s ease; transform-style: preserve-3d; pointer-events: none; width: 100%; @@ -1143,7 +1401,7 @@ .hoverRoll:hover .hoverRoll_normal, .colorwaysSettings-colorwaySource:hover .hoverRoll_normal { - transform: translate3d(0,-107%,0); + transform: translate3d(0, -107%, 0); opacity: 0; user-select: none; } @@ -1161,6 +1419,7 @@ width: 90vw !important; height: 90vh !important; max-height: unset !important; + animation: show-modal 0.2s ease; } .colorwaysPresetPicker-content { @@ -1175,10 +1434,9 @@ display: flex; flex-direction: row; justify-content: space-between; - border-radius: 4px; - background-color: var(--background-secondary); + border-radius: 8px; + background-color: #1a1a1a; box-sizing: border-box; - color: var(--header-secondary); padding: 10px 18px; padding-right: 10px; cursor: pointer; @@ -1186,31 +1444,31 @@ } .colorwaysCreator-setting:hover { - background-color: var(--background-modifier-hover); + background-color: #2a2a2a; } .dc-colorway-selector::before { /* stylelint-disable-next-line property-no-vendor-prefix */ -webkit-mask: var(--si-appearance) center/contain no-repeat !important; - mask: var(--si-appearance) center/contain no-repeat !important + mask: var(--si-appearance) center/contain no-repeat !important; } .dc-colorway-settings::before { /* stylelint-disable-next-line property-no-vendor-prefix */ - -webkit-mask: var(--si-equicordsettings) center/contain no-repeat !important; - mask: var(--si-equicordsettings) center/contain no-repeat !important + -webkit-mask: var(--si-vencordsettings) center/contain no-repeat !important; + mask: var(--si-vencordsettings) center/contain no-repeat !important; } .dc-colorway-ondemand::before { /* stylelint-disable-next-line property-no-vendor-prefix */ - -webkit-mask: var(--si-equicordupdater) center/contain no-repeat !important; - mask: var(--si-equicordupdater) center/contain no-repeat !important + -webkit-mask: var(--si-vencordupdater) center/contain no-repeat !important; + mask: var(--si-vencordupdater) center/contain no-repeat !important; } .dc-colorway-sources-manager::before { /* stylelint-disable-next-line property-no-vendor-prefix */ -webkit-mask: var(--si-instantinvites) center/contain no-repeat !important; - mask: var(--si-instantinvites) center/contain no-repeat !important + mask: var(--si-instantinvites) center/contain no-repeat !important; } .colorwaySourceModal { @@ -1241,6 +1499,15 @@ .colorwaysSettings-sourceScroller { scrollbar-width: none; + display: flex; + flex-direction: column; + gap: 8px; + overflow: hidden auto; +} + +.colorwaysScroller { + scrollbar-width: none !important; + overflow: hidden auto; } .colorwaysSettings-sourceScroller::-webkit-scrollbar { @@ -1249,8 +1516,9 @@ .colorwayMessage { padding: 20px; - border: 1px solid; - border-radius: 5px; + border: 1px solid #a6a6a6f0; + border-radius: 8px; + background-color: #090909; display: flex; } @@ -1259,20 +1527,6 @@ flex-direction: column; } -.theme-dark .colorwayMessage { - background: hsl(var(--primary-630-hsl)/60%); - border-color: hsl(var(--primary-630-hsl)/90%); -} - -.theme-light .colorwayMessage { - background: hsl(var(--primary-100-hsl)/60%); - border-color: hsl(var(--primary-200-hsl)/30%); -} - -.colorwaySelector-sources_settings { - margin-left: 8px; -} - .colorwaysLoadingModal, .colorwayInfo-cssModal { width: fit-content; @@ -1284,28 +1538,12 @@ border: none; } -.ColorwaySelectorWrapper-list { - display: flex; - flex-direction: column; - gap: 0; - flex-wrap: nowrap; - padding-bottom: 0; -} - -.discordColorway-listItem .discordColorwayPreviewColorContainer { +.discordColorway .discordColorwayPreviewColorContainer { width: 30px; height: 30px; } -.colorwayLabel.labelInGrid { - max-height: 2rem; - overflow: hidden; - text-overflow: ellipsis; - margin-top: 4px; - text-align: center; -} - -.discordColorway-listItem .colorwayInfoIconContainer { +.discordColorway .colorwayInfoIconContainer { height: 28px; width: 28px; border-radius: 3px; @@ -1317,19 +1555,20 @@ background: transparent; border: 1px solid var(--button-outline-primary-border); color: var(--button-outline-primary-text); - transition: .15s; + transition: 0.15s; } -.discordColorway-listItem .colorwayInfoIconContainer:hover { +.discordColorway .colorwayInfoIconContainer:hover { background-color: var(--button-outline-primary-background-hover); border-color: var(--button-outline-primary-border-hover); color: var(--button-outline-primary-text-hover); } -.colorwayLabel:not(.labelInGrid) { +.colorwayLabel { margin-right: auto; margin-top: 0 !important; - margin-left: .5rem; + margin-left: 0.5rem; + color: #dfdfdf; } .colorwaySelectionCircle { @@ -1340,10 +1579,6 @@ left: 0; } -.ColorwaySelectorWrapper-grid { - margin-bottom: 16px; -} - .colorwaySelector-sorter { height: 50px; width: 100%; @@ -1377,3 +1612,231 @@ height: 8px; border-radius: 16px; } + +.colorwaysModal { + border-radius: 16px; + background-color: #000; + color: #fff; + height: fit-content; + min-height: unset; + width: fit-content; + border: none; + padding: 0; + margin: 0; + transition: 0.4s ease; + animation: show-modal 0.4s ease; + pointer-events: all; +} + +.colorwaysModalContent { + display: flex; + flex-direction: column; + gap: 4px; + padding: 16px; +} + +.colorwaysModalContent-sourcePreview { + padding-left: 0; + padding-right: 0; +} + +.colorwaysMenuTabs { + width: 100%; + height: 30px; + padding-bottom: 8px; + box-sizing: content-box; +} + +.colorwaysMenuTab { + color: #fff; + text-decoration: none; + padding: 4px 12px; + border-radius: 32px; + transition: 0.2s ease; + margin-right: 8px; + display: inline-block; +} + +.colorwaySourceTab { + box-sizing: border-box; + width: 100%; + display: flex; + flex-direction: column; + gap: 8px; +} + +.colorwaysMenuTab:hover { + background-color: #1f1f1f; +} + +.colorwaysMenuTab.active { + color: #000; + background-color: #fff; +} + +.colorwaysModalFooter { + border-top-left-radius: 8px; + border-top-right-radius: 8px; + border-bottom-left-radius: 16px; + border-bottom-right-radius: 16px; + padding: 8px; + display: flex; + flex-direction: row-reverse; + background-color: #0a0a0a; + width: calc(100% - 16px); + gap: 8px; +} + +.colorwaysModalFooter>.colorwaysPillButton { + width: 100%; +} + +.colorwaysModalHeader { + margin: 0; + font-weight: normal; + font-size: 1.25em; + padding: 16px; +} + +.colorwaysModalSectionHeader { + font-size: 14px; + margin-bottom: 2px; +} + +.colorwaysModalSectionError { + color: red; + font-style: italic; +} + +.colorwayIDCard { + display: flex; + flex-direction: column; + gap: 1em; +} + +.colorwaysContextMenu { + border-radius: 8px; + border: 1px solid #dfdfdf; + background-color: #000; + padding: 4px; + display: flex; + flex-direction: column; + gap: 4px; + z-index: 5; +} + +.colorwaysContextMenuItm { + box-sizing: border-box; + display: flex; + justify-content: space-between; + align-items: center; + min-height: 32px; + padding: 6px 8px; + border-radius: 6px; + background-color: #101010; + border: 1px solid transparent; + transition: 0.2s ease; + cursor: pointer; + color: #dfdfdf; +} + +.colorwaysContextMenuItm:hover { + background-color: #2a2a2a; + border-color: #a6a6a6; +} + +.colorwaysRadioSelected { + fill: #fff; +} + +.colorwaysTooltip { + background-color: var(--background-floating); + box-shadow: var(--shadow-high); + color: var(--text-normal); + pointer-events: none; + border-radius: 5px; + font-weight: 500; + font-size: 14px; + line-height: 16px; + max-width: 190px; + box-sizing: border-box; + word-wrap: break-word; + z-index: 1002; + will-change: opacity, transform; + transition: + transform 0.1s ease, + opacity 0.1s ease; + position: fixed; +} + +.colorwaysTooltip.colorwaysTooltip-hidden { + transform: scale(0.95); + opacity: 0; +} + +.colorwaysTooltip-right { + transform-origin: 0% 50%; +} + +.colorwaysTooltipPointer { + width: 0; + height: 0; + border: 0 solid transparent; + border-width: 5px; + pointer-events: none; + border-top-color: var(--background-floating); +} + +.colorwaysTooltip-right>.colorwaysTooltipPointer { + position: absolute; + right: 100%; + top: 50%; + margin-top: -5px; + border-left-width: 5px; + transform: rotate(90deg); +} + +.colorwaysTooltipContent { + padding: 8px 12px; + overflow: hidden; + font-weight: 600; + font-size: 16px; + line-height: 20px; + display: flex; + flex-direction: column; +} + +.colorwaysManagerConnectionMenu { + transform: translateX(-20px); + opacity: 0; + border: 1px solid #a6a6a6f0; + background-color: #090909; + transition: + transform 0.2s ease, + opacity 0.2s ease; + display: flex; + flex-direction: column; + padding: 8px 12px; + color: #fff; + pointer-events: none; + border-radius: 8px; + font-weight: 600; + font-size: 16px; + line-height: 20px; +} + +.colorwaysManagerConnectionMenu.visible { + opacity: 1; + transform: none; + pointer-events: all; +} + +.colorwaysManagerConnectionValue { + color: #80868e; + font-weight: 500; + font-size: 12; +} + +.colorwaysManagerConnectionValue>b { + color: #a0a6ae; +} diff --git a/src/equicordplugins/discordColorways/theme.discord.css b/src/equicordplugins/discordColorways/theme.discord.css new file mode 100644 index 00000000..123ce223 --- /dev/null +++ b/src/equicordplugins/discordColorways/theme.discord.css @@ -0,0 +1,333 @@ +/* stylelint-disable color-function-notation */ +.colorwaySelectorModal[data-theme="discord"], +.colorwayModal[data-theme="discord"] { + border: none; + box-shadow: var(--legacy-elevation-border), var(--legacy-elevation-high); + background-color: var(--modal-background); +} + +[data-theme="discord"] .colorwaysSettingsDivider { + border-color: var(--background-modifier-accent); +} + +[data-theme="discord"] .colorwaySwitch-label, +[data-theme="discord"] .colorwaysNote { + color: var(--header-primary); +} + +[data-theme="discord"] .colorwaysSettings-switchCircle { + fill: #fff !important; +} + +[data-theme="discord"] .colorwaysSettings-switch { + background-color: rgb(128, 132, 142); +} + +[data-theme="discord"] .colorwaysSettings-switch.checked { + background-color: #23a55a; +} + +[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab { + transition: none; + border-radius: 4px; + border: none; +} + +[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab.active { + background-color: var(--background-modifier-selected); +} + +[data-theme="discord"] > .colorwaySelectorSidebar > .colorwaySelectorSidebar-tab:hover { + background-color: var(--background-modifier-hover); +} + +[data-theme="discord"] .colorwaysPillButton { + color: var(--white-500); + background-color: var(--button-secondary-background); + height: var(--custom-button-button-sm-height); + min-width: var(--custom-button-button-sm-width); + min-height: var(--custom-button-button-sm-height); + width: auto; + transition: + background-color var(--custom-button-transition-duration) ease, + color var(--custom-button-transition-duration) ease; + position: relative; + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + border: none; + border-radius: 3px; + font-size: 14px; + font-weight: 500; + line-height: 16px; + padding: 2px 16px; + user-select: none; +} + +[data-theme="discord"] .colorwaysPillButton:hover { + background-color: var(--button-secondary-background-hover); +} + +[data-theme="discord"] > .colorwaySelectorSidebar { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + background-color: var(--modal-footer-background); + box-shadow: inset 0 1px 0 hsl(var(--primary-630-hsl) / 60%); + padding: 12px; +} + +[data-theme="discord"] .colorwaySelector-search { + border-radius: 3px; + color: var(--text-normal); + background-color: var(--input-background) !important; + height: 40px; + padding: 10px; + transition: none; + font-size: 16px; + border: none; +} + +[data-theme="discord"] .colorwaysSettings-colorwaySource { + border-radius: 4px; + color: var(--interactive-normal); + background-color: var(--background-secondary); +} + +[data-theme="discord"] .colorwaysSettings-colorwaySource:hover { + color: var(--interactive-active); + background-color: var(--background-modifier-hover); +} + +[data-theme="discord"] .discordColorway { + border-radius: 4px; + transition: none; + background-color: var(--background-secondary); + border: none; +} + +[data-theme="discord"] .discordColorway:hover { + filter: none; + background-color: var(--background-modifier-hover); +} + +[data-theme="discord"] .discordColorway[aria-checked="true"] { + background-color: var(--background-modifier-selected); +} + +[data-theme="discord"] .colorwaysSettings-colorwaySourceLabelHeader, +[data-theme="discord"] .colorwaysSettings-colorwaySourceDesc { + color: var(--header-primary); +} + +[data-theme="discord"] .colorways-badge { + height: 16px; + padding: 0 4px; + border-radius: 4px; + margin-left: 4px; + flex: 0 0 auto; + background: var(--bg-brand); + color: var(--white); + text-transform: uppercase; + vertical-align: top; + display: inline-flex; + align-items: center; + text-indent: 0; + font-weight: 600; + font-size: 12px; + line-height: 16px; +} + +.colorwaysModal[data-theme="discord"] { + box-shadow: var(--legacy-elevation-border), var(--legacy-elevation-high); + background-color: var(--modal-background); + border-radius: 4px; + display: flex; + flex-direction: column; + margin: 0 auto; + pointer-events: all; + position: relative; +} + +[data-theme="discord"] .colorwaysMenuTabs { + padding-bottom: 16px; +} + +[data-theme="discord"] .colorwaysMenuTab { + padding: 0; + padding-bottom: 16px; + margin-right: 32px; + margin-bottom: -2px; + border-bottom: 2px solid transparent; + transition: none; + border-radius: 0; + background-color: transparent; + font-size: 16px; + line-height: 20px; + cursor: pointer; + font-weight: 500; +} + +[data-theme="discord"] .colorwaysMenuTab:hover { + color: var(--interactive-hover); + border-bottom-color: var(--brand-500); +} + +[data-theme="discord"] .colorwaysMenuTab.active { + cursor: default; + color: var(--interactive-active); + border-bottom-color: var(--control-brand-foreground); +} + +[data-theme="discord"] .colorwaysModalFooter { + border-radius: 0 0 5px 5px; + background-color: var(--modal-footer-background); + padding: 16px; + box-shadow: inset 0 1px 0 hsl(var(--primary-630-hsl) / 60%); + gap: 0; + width: unset; +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton { + width: auto; + height: var(--custom-button-button-md-height); + min-width: var(--custom-button-button-md-width); + min-height: var(--custom-button-button-md-height); + transition: + color var(--custom-button-transition-duration) ease, + background-color var(--custom-button-transition-duration) ease, + border-color var(--custom-button-transition-duration) ease; + border: 1px solid var(--button-outline-primary-border); + color: var(--button-outline-primary-text); + margin-left: 8px; + background-color: transparent; +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton:hover { + background-color: var(--button-outline-primary-background-hover); + border-color: var(--button-outline-primary-border-hover); + color: var(--button-outline-primary-text-hover); +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton:active { + background-color: var(--button-outline-primary-background-active); + border-color: var(--button-outline-primary-border-active); + color: var(--button-outline-primary-text-active); +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface { + color: var(--white-500); + background-color: var(--brand-500); + border: none; +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface:hover { + background-color: var(--brand-560); +} + +[data-theme="discord"] .colorwaysModalFooter > .colorwaysPillButton.colorwaysPillButton-onSurface:active { + background-color: var(--brand-600); +} + +[data-theme="discord"] .colorwaysModalHeader { + box-shadow: + 0 1px 0 0 hsl(var(--primary-800-hsl) / 30%), + 0 1px 2px 0 hsl(var(--primary-800-hsl) / 30%); + border-radius: 4px 4px 0 0; + transition: box-shadow 0.1s ease-out; + word-wrap: break-word; +} + +[data-theme="discord"] .colorwaysModalSectionHeader, +[data-theme="discord"] .colorwaysSettings-colorwaySourceLabel, +[data-theme="discord"] .colorwaysSettings-colorwaySourceLabelHeader, +[data-theme="discord"] .colorwaysSettings-colorwaySourceDesc { + color: var(--header-primary); +} + +[data-theme="discord"] .colorwaysCreator-setting, +[data-theme="discord"] .colorwaysCreator-settingCat { + border-radius: 4px; + background-color: var(--background-secondary); +} + +[data-theme="discord"] .colorwaysCreator-setting:hover { + background-color: var(--background-modifier-hover); +} + +[data-theme="discord"] .colorwaysContextMenu { + background: var(--background-floating); + box-shadow: var(--shadow-high); + border-radius: 4px; + padding: 6px 8px; + border: none; + gap: 0; + min-width: 188px; + max-width: 320px; + box-sizing: border-box; +} + +[data-theme="discord"] .colorwaysContextMenuItm { + border: none; + transition: none; + margin: 2px 0; + border-radius: 2px; + font-size: 14px; + font-weight: 500; + line-height: 18px; + color: var(--interactive-normal); + background-color: transparent; +} + +[data-theme="discord"] .colorwaysContextMenuItm:hover { + background-color: var(--menu-item-default-hover-bg); + color: var(--white); +} + +[data-theme="discord"] .colorwaysContextMenuItm:active { + background-color: var(--menu-item-default-active-bg); + color: var(--white); +} + +[data-theme="discord"] .colorwaysRadioSelected { + fill: var(--control-brand-foreground-new); +} + +[data-theme="discord"] .colorwaysConflictingColors-warning { + color: var(--text-normal); +} + +[data-theme="discord"] .colorwaysManagerConnectionMenu { + transition: + transform 0.1s ease, + opacity 0.1s ease; + transform: scale(0.95); + transform-origin: 0% 50%; + background-color: var(--background-floating); + box-shadow: var(--shadow-high); + color: var(--text-normal); + border: none; + border-radius: 5px; +} + +.colorwayIDCard[data-theme="discord"] > .colorwayMessage { + border-radius: 5px; + border: none; + background-color: var(--background-secondary); +} + +.theme-dark .colorwayIDCard[data-theme="discord"] .colorwayMessage { + background: hsl(var(--primary-630-hsl) / 60%); +} + +.theme-light .colorwayIDCard[data-theme="discord"] .colorwayMessage { + background: hsl(var(--primary-100-hsl) / 60%); +} + +[data-theme="discord"] .colorwaysManagerConnectionValue { + color: var(--text-muted); +} + +[data-theme="discord"] .colorwaysManagerConnectionValue > b { + color: var(--text-normal); +} diff --git a/src/equicordplugins/discordColorways/types.ts b/src/equicordplugins/discordColorways/types.ts index dbc09aa4..0678e31f 100644 --- a/src/equicordplugins/discordColorways/types.ts +++ b/src/equicordplugins/discordColorways/types.ts @@ -21,7 +21,8 @@ export interface Colorway { source?: string, linearGradient?: string, preset?: string, - creatorVersion: string; + creatorVersion: string, + colorObj?: { accent?: string, primary?: string, secondary?: string, tertiary?: string; }; } export interface ColorPickerProps { @@ -34,9 +35,15 @@ export interface ColorPickerProps { export interface ColorwayObject { id: string | null, - css: string | null, + css?: string | null, sourceType: "online" | "offline" | "temporary" | null, - source: string | null | undefined; + source: string | null | undefined, + colors?: { + accent?: string | undefined, + primary?: string | undefined, + secondary?: string | undefined, + tertiary?: string | undefined; + } | undefined; } export interface SourceObject { @@ -63,3 +70,8 @@ export interface StoreItem { url: string, authorGh: string; } + +export interface ModalProps { + transitionState: 0 | 1 | 2 | 3 | 4; + onClose(): void; +} diff --git a/src/equicordplugins/discordColorways/utils.ts b/src/equicordplugins/discordColorways/utils.ts index 62e3859e..869f82e4 100644 --- a/src/equicordplugins/discordColorways/utils.ts +++ b/src/equicordplugins/discordColorways/utils.ts @@ -148,3 +148,84 @@ export function colorToHex(color: string) { } return color.replace("#", ""); } + +export const parseClr = (clr: number) => (clr & 0x00ffffff).toString(16).padStart(6, "0"); + +export async function getRepainterTheme(link: string): Promise<{ status: "success" | "fail", id?: string, colors?: string[], errorCode?: number, errorMsg?: string; }> { + const linkCheck: string | undefined = link.match(/https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_+.~#?&//=]*)/g)!.filter(x => x.startsWith("https://repainter.app/themes/"))[0]; + + if (!linkCheck) return { status: "fail", errorCode: 0, errorMsg: "Invalid URL" }; + + // const res = await ( + // await fetch( + // `https://repainter.app/_next/data/Z0BCpVYZyrdkss0k0zqLC/themes/${link.match(/themes\/([a-z0-9]+)/i)?.[1] ?? "" + // }.json`, + // { + // "headers": { + // "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7", + // "accept-language": "en-US,en;q=0.9", + // "if-none-match": "W/\"4b2-Wsw1gFTK1l04ijqMn5s6ZUnH6hM\"", + // "priority": "u=0, i", + // "sec-ch-ua": "\"Chromium\";v=\"125\", \"Not.A/Brand\";v=\"24\"", + // "sec-ch-ua-mobile": "?0", + // "sec-ch-ua-platform": "\"Linux\"", + // "sec-fetch-dest": "document", + // "sec-fetch-mode": "navigate", + // "sec-fetch-site": "none", + // "sec-fetch-user": "?1", + // "upgrade-insecure-requests": "1" + // }, + // "referrerPolicy": "strict-origin-when-cross-origin", + // "body": null, + // "method": "GET", + // "mode": "cors", + // "credentials": "omit", + // "cache": "no-store" + // }, + // ) + // ); + const { pageProps: { fallback: { a: { name, colors } } } } = { "pageProps": { "initialId": "01G5PMR5G9H76H1R2RET4A0ZHY", "fallback": { a: { "id": "01G5PMR5G9H76H1R2RET4A0ZHY", "name": "Midwinter Fire", "description": "Very red", "createdAt": "2022-06-16T16:15:11.881Z", "updatedAt": "2022-07-12T08:37:13.141Z", "settingsLines": ["Colorful", "Bright", "Vibrant style"], "voteCount": 309, "colors": [-1426063361, 4294901760, 4294901760, -1426071591, -1426080078, -1426089335, 4294901760, -1426119398, -1428615936, -1431629312, -1434644480, 4294901760, 4294901760, 4294901760, 4294901760, -1426067223, -1426071086, -1426079070, -1426088082, 4294901760, -1428201216, -1430761216, -1433255936, 4294901760, 4294901760, 4294901760, 4294901760, 4294901760, 4294901760, -1426070330, 4294901760, -1426086346, 4294901760, -1430030080, 4294901760, -1434431744, 4294901760, 4294901760, 4294901760, 4294901760, -1426064133, 4294901760, -1426071591, 4294901760, -1426874223, 4294901760, -1430359452, 4294901760, -1433845194, 4294901760, -1437922816, 4294901760, 4294901760, 4294901760, 4294901760, -1426071591, -1426080078, -1426089335, -1427799438, -1429640356, 4294901760, -1433191891, 4294901760, 4294901760, 4294901760] } } }, "__N_SSP": true } as any; + return { status: "success", id: name, colors: colors.filter(c => c !== 4294901760).map(c => "#" + parseClr(c)) }; +} + +/** + * Prompts the user to choose a file from their system + * @param mimeTypes A comma separated list of mime types to accept, see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers + * @returns A promise that resolves to the chosen file or null if the user cancels + */ +export function chooseFile(mimeTypes: string) { + return new Promise(resolve => { + const input = document.createElement("input"); + input.type = "file"; + input.style.display = "none"; + input.accept = mimeTypes; + input.onchange = async () => { + resolve(input.files?.[0] ?? null); + }; + + document.body.appendChild(input); + input.click(); + setImmediate(() => document.body.removeChild(input)); + }); +} + +/** + * Prompts the user to save a file to their system + * @param file The file to save + */ +export function saveFile(file: File) { + const a = document.createElement("a"); + a.href = URL.createObjectURL(file); + a.download = file.name; + + document.body.appendChild(a); + a.click(); + setImmediate(() => { + URL.revokeObjectURL(a.href); + document.body.removeChild(a); + }); +} + +export function classes(...classes: Array) { + return classes.filter(Boolean).join(" "); +} diff --git a/src/equicordplugins/discordColorways/wsClient.ts b/src/equicordplugins/discordColorways/wsClient.ts new file mode 100644 index 00000000..11b43436 --- /dev/null +++ b/src/equicordplugins/discordColorways/wsClient.ts @@ -0,0 +1,229 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "."; +import { ColorwayCSS } from "./colorwaysAPI"; +import { updateBoundKeyMain, updateWSMain } from "./components/MainModal"; +import { updateActiveColorway, updateManagerRole, updateWS as updateWSSelector } from "./components/Selector"; +import { nullColorwayObj } from "./constants"; +import { generateCss } from "./css"; +import { ColorwayObject } from "./types"; +import { colorToHex } from "./utils"; + +export let wsOpen = false; +export let boundKey: { [managerKey: string]: string; } | null = null; +export let hasManagerRole: boolean = false; + +export let sendColorway: (obj: ColorwayObject) => void = () => { }; +export let requestManagerRole: () => void = () => { }; +export let updateRemoteSources: () => void = () => { }; +export let closeWS: () => void = () => { }; +export let restartWS: () => void = () => connect(); +export let updateShouldAutoconnect: (shouldAutoconnect: boolean) => void = () => connect(); + +function updateWS(status: boolean) { + updateWSSelector(status); + updateWSMain(status); +} + +function updateBoundKey(bound: { [managerKey: string]: string; }) { + updateBoundKeyMain(bound); +} + +export function connect() { + var ws: WebSocket | null = new WebSocket("ws://localhost:6124"); + + updateShouldAutoconnect = shouldAutoconnect => { + if (shouldAutoconnect && ws?.readyState === ws?.CLOSED) connect(); + }; + + ws.onopen = function () { + wsOpen = true; + hasManagerRole = false; + updateWS(true); + }; + + restartWS = () => { + ws?.close(); + connect(); + }; + closeWS = () => ws?.close(); + + ws.onmessage = function (e) { + const data: { + type: "change-colorway" | "remove-colorway" | "manager-connection-established" | "complication:remote-sources:received" | "complication:remote-sources:update-request" | "complication:manager-role:granted" | "complication:manager-role:revoked", + [key: string]: any; + } = JSON.parse(e.data); + + function typeSwitch(type) { + switch (type) { + case "change-colorway": + if (data.active.id == null) { + DataStore.set("activeColorwayObject", nullColorwayObj); + ColorwayCSS.remove(); + updateActiveColorway(nullColorwayObj); + } else { + const demandedColorway = generateCss( + colorToHex("#" + data.active.colors.primary || "#313338").replace("#", ""), + colorToHex("#" + data.active.colors.secondary || "#2b2d31").replace("#", ""), + colorToHex("#" + data.active.colors.tertiary || "#1e1f22").replace("#", ""), + colorToHex("#" + data.active.colors.accent || "#5865f2").replace("#", "") + ); + ColorwayCSS.set(demandedColorway); + DataStore.set("activeColorwayObject", { ...data.active, css: demandedColorway }); + updateActiveColorway({ ...data.active, css: demandedColorway }); + } + return; + case "remove-colorway": + DataStore.set("activeColorwayObject", nullColorwayObj); + ColorwayCSS.remove(); + updateActiveColorway(nullColorwayObj); + return; + case "manager-connection-established": + DataStore.get("colorwaysBoundManagers").then((boundManagers: { [managerKey: string]: string; }[]) => { + if (data.MID) { + const boundSearch = boundManagers.filter(boundManager => { + if (Object.keys(boundManager)[0] === data.MID) return boundManager; + }); + if (boundSearch.length) { + boundKey = boundSearch[0]; + } else { + const id = { [data.MID]: `vencord.${Math.random().toString(16).slice(2)}.${new Date().getUTCMilliseconds()}` }; + DataStore.set("colorwaysBoundManagers", [...boundManagers, id]); + boundKey = id; + } + updateBoundKey(typeof boundKey === "string" ? JSON.parse(boundKey) : boundKey); + ws?.send(JSON.stringify({ + type: "client-sync-established", + boundKey, + complications: [ + "remote-sources", + "manager-role" + ] + })); + DataStore.getMany([ + "colorwaySourceFiles", + "customColorways" + ]).then(([ + colorwaySourceFiles, + customColorways + ]) => { + ws?.send(JSON.stringify({ + type: "complication:remote-sources:init", + boundKey, + online: colorwaySourceFiles, + offline: customColorways + })); + }); + sendColorway = obj => ws?.send(JSON.stringify({ + type: "complication:manager-role:send-colorway", + active: obj, + boundKey + })); + requestManagerRole = () => ws?.send(JSON.stringify({ + type: "complication:manager-role:request", + boundKey + })); + updateRemoteSources = () => DataStore.getMany([ + "colorwaySourceFiles", + "customColorways" + ]).then(([ + colorwaySourceFiles, + customColorways + ]) => { + ws?.send(JSON.stringify({ + type: "complication:remote-sources:init", + boundKey, + online: colorwaySourceFiles, + offline: customColorways + })); + }); + } + }); + return; + case "complication:manager-role:granted": + hasManagerRole = true; + updateManagerRole(true); + return; + case "complication:manager-role:revoked": + hasManagerRole = false; + updateManagerRole(false); + return; + case "complication:remote-sources:update-request": + DataStore.getMany([ + "colorwaySourceFiles", + "customColorways" + ]).then(([ + colorwaySourceFiles, + customColorways + ]) => { + ws?.send(JSON.stringify({ + type: "complication:remote-sources:init", + boundKey, + online: colorwaySourceFiles, + offline: customColorways + })); + }); + return; + } + } + + typeSwitch(data.type); + }; + + ws.onclose = function (e) { + boundKey = null; + hasManagerRole = false; + sendColorway = () => { }; + requestManagerRole = () => { }; + updateRemoteSources = () => { }; + restartWS = () => connect(); + closeWS = () => { }; + try { + ws?.close(); + } catch (e) { + return; + } + ws = null; + wsOpen = false; + updateWS(false); + DataStore.getMany([ + "colorwaysManagerAutoconnectPeriod", + "colorwaysManagerDoAutoconnect" + ]).then(([ + colorwaysManagerAutoconnectPeriod, + colorwaysManagerDoAutoconnect + ]) => { + // eslint-disable-next-line no-constant-condition + if (colorwaysManagerDoAutoconnect || true) setTimeout(() => connect(), colorwaysManagerAutoconnectPeriod || 3000); + }); + }; + + ws.onerror = function (e) { + e.preventDefault(); + boundKey = null; + sendColorway = () => { }; + requestManagerRole = () => { }; + updateRemoteSources = () => { }; + restartWS = () => connect(); + closeWS = () => { }; + hasManagerRole = false; + ws?.close(); + ws = null; + wsOpen = false; + updateWS(false); + DataStore.getMany([ + "colorwaysManagerAutoconnectPeriod", + "colorwaysManagerDoAutoconnect" + ]).then(([ + colorwaysManagerAutoconnectPeriod, + colorwaysManagerDoAutoconnect + ]) => { + // eslint-disable-next-line no-constant-condition + if (colorwaysManagerDoAutoconnect || true) setTimeout(() => connect(), colorwaysManagerAutoconnectPeriod || 3000); + }); + }; +} diff --git a/src/equicordplugins/statusWhilePlaying.desktop/index.ts b/src/equicordplugins/statusWhilePlaying.desktop/index.ts index e5af9eef..0e204457 100644 --- a/src/equicordplugins/statusWhilePlaying.desktop/index.ts +++ b/src/equicordplugins/statusWhilePlaying.desktop/index.ts @@ -47,13 +47,13 @@ export default definePlugin({ RUNNING_GAMES_CHANGE(event) { const status = PresenceStore.getStatus(UserStore.getCurrentUser().id); if (event.games.length > 0) { - if (savedStatus !== "" && savedStatus !== settings.store.statusToSet) - updateAsync(savedStatus); - } else { if (status !== settings.store.statusToSet) { savedStatus = status; updateAsync(settings.store.statusToSet); } + } else { + if (savedStatus !== "" && savedStatus !== settings.store.statusToSet) + updateAsync(savedStatus); } }, }