mirror of
https://github.com/Equicord/Equicord.git
synced 2025-06-13 08:33:01 -04:00
Add customUserColors (#150)
* add preferFriends to showMeYourName * Add customUserColors * pressing enter closes modal * please update your discord types package * sowwy.. * Do A Settings Check For Typing Tweaks * Do a proper import on rolecoloreverywhere --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
f385879b4b
commit
7ca3bbb8e6
5 changed files with 244 additions and 3 deletions
95
src/equicordplugins/customUserColors/SetColorModal.tsx
Normal file
95
src/equicordplugins/customUserColors/SetColorModal.tsx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2025 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { set } from "@api/DataStore";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot } from "@utils/modal";
|
||||||
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
import { Button, Forms, useState } from "@webpack/common";
|
||||||
|
|
||||||
|
import { colors, DATASTORE_KEY } from "./index";
|
||||||
|
|
||||||
|
interface ColorPickerProps {
|
||||||
|
color: number;
|
||||||
|
showEyeDropper?: boolean;
|
||||||
|
suggestedColors?: string[];
|
||||||
|
onChange(value: number | null): void;
|
||||||
|
}
|
||||||
|
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||||
|
|
||||||
|
const cl = classNameFactory("vc-customColors-");
|
||||||
|
|
||||||
|
export function SetColorModal({ userId, modalProps }: { userId: string, modalProps: ModalProps; }) {
|
||||||
|
|
||||||
|
const userColor = colors[userId];
|
||||||
|
|
||||||
|
const initialColor = parseInt(colors[userId], 16) || 372735;
|
||||||
|
// color picker default to current color set for user (if null it's 0x05afff :3 )
|
||||||
|
|
||||||
|
const [colorPickerColor, setColorPickerColor] = useState(initialColor);
|
||||||
|
// hex color code as an int (NOT rgb 0-255)
|
||||||
|
|
||||||
|
|
||||||
|
function setUserColor(color: number) {
|
||||||
|
setColorPickerColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKey(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Enter")
|
||||||
|
saveUserColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveUserColor() {
|
||||||
|
colors[userId] = colorPickerColor.toString(16).padStart(6, "0");
|
||||||
|
await set(DATASTORE_KEY, colors);
|
||||||
|
modalProps.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteUserColor() {
|
||||||
|
delete colors[userId];
|
||||||
|
await set(DATASTORE_KEY, colors);
|
||||||
|
modalProps.onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalRoot {...modalProps}>
|
||||||
|
<ModalHeader className={cl("modal-header")}>
|
||||||
|
<Forms.FormTitle tag="h2">
|
||||||
|
Custom Color
|
||||||
|
</Forms.FormTitle>
|
||||||
|
<ModalCloseButton onClick={modalProps.onClose} />
|
||||||
|
</ModalHeader>
|
||||||
|
<ModalContent className={cl("modal-content")} onKeyDown={handleKey}>
|
||||||
|
<section className={Margins.bottom16}>
|
||||||
|
<Forms.FormTitle tag="h3">
|
||||||
|
Pick a color
|
||||||
|
</Forms.FormTitle>
|
||||||
|
<ColorPicker
|
||||||
|
color={colorPickerColor}
|
||||||
|
onChange={setUserColor}
|
||||||
|
showEyeDropper={false}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<ModalFooter className={cl("modal-footer")}>
|
||||||
|
<Button
|
||||||
|
color={Button.Colors.RED}
|
||||||
|
onClick={deleteUserColor}
|
||||||
|
>
|
||||||
|
Delete Entry
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color={Button.Colors.BRAND}
|
||||||
|
onClick={saveUserColor}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalRoot>
|
||||||
|
);
|
||||||
|
}
|
119
src/equicordplugins/customUserColors/index.tsx
Normal file
119
src/equicordplugins/customUserColors/index.tsx
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2025 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "./styles.css";
|
||||||
|
|
||||||
|
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||||
|
import { get } from "@api/DataStore";
|
||||||
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
|
import { EquicordDevs } from "@utils/constants";
|
||||||
|
import { openModal } from "@utils/modal";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { extractAndLoadChunksLazy } from "@webpack";
|
||||||
|
import { Menu, UserStore } from "@webpack/common";
|
||||||
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
import { SetColorModal } from "./SetColorModal";
|
||||||
|
|
||||||
|
export const DATASTORE_KEY = "equicord-customcolors";
|
||||||
|
export let colors: Record<string, string> = {};
|
||||||
|
(async () => {
|
||||||
|
colors = await get<Record<string, string>>(DATASTORE_KEY) || {};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/);
|
||||||
|
// needed for color picker to be available without opening settings (ty pindms!!)
|
||||||
|
const userContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: { user: User; }) => {
|
||||||
|
if (user?.id == null) return;
|
||||||
|
|
||||||
|
const setCustomColorItem = (
|
||||||
|
<Menu.MenuItem
|
||||||
|
label="Set Color"
|
||||||
|
id="set-color"
|
||||||
|
action={async () => {
|
||||||
|
await requireSettingsMenu();
|
||||||
|
openModal(modalProps => <SetColorModal userId={user.id} modalProps={modalProps} />);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
children.push(<Menu.MenuSeparator />, setCustomColorItem);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getCustomColorString(userId: string, withHash?: boolean): string | undefined {
|
||||||
|
if (!colors[userId] || !Settings.plugins.customUserColors.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (withHash)
|
||||||
|
return `#${colors[userId]}`;
|
||||||
|
|
||||||
|
return colors[userId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
DmList: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "Users with custom colors defined will have their name in the dm list colored",
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
colorInServers: {
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
description: "If name colors should be changed within servers",
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "customUserColors",
|
||||||
|
description: "Lets you add a custom color to any user, anywhere! Highly recommend to use with typingTweaks and roleColorEverywhere",
|
||||||
|
authors: [EquicordDevs.mochienya],
|
||||||
|
contextMenus: { "user-context": userContextMenuPatch },
|
||||||
|
settings,
|
||||||
|
requireSettingsMenu,
|
||||||
|
getCustomColorString,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
// this also affects name headers in chats outside of servers
|
||||||
|
find: /type:\i\.\i\.Types\.REMIX/,
|
||||||
|
replacement: {
|
||||||
|
match: /style:"username".*?void 0/,
|
||||||
|
replace: "style:{color:$self.colorIfServer(arguments[0])}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
predicate: () => settings.store.DmList,
|
||||||
|
find: /muted:\i=!1,highlighted:\i=!1/,
|
||||||
|
replacement: {
|
||||||
|
match: /(nameAndDecorators,)/,
|
||||||
|
replace: "$1style:{color:$self.colorDMList(arguments[0])},"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
colorDMList(a: any): string | undefined {
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
const { id } = UserStore.findByTag(a.avatar.props["aria-label"]);
|
||||||
|
// get user id by props on avatars having username as aria label
|
||||||
|
const colorString = getCustomColorString(id, true);
|
||||||
|
if (colorString)
|
||||||
|
return colorString;
|
||||||
|
return "inherit";
|
||||||
|
} catch { return; } // if you have a group in your dms then discord will crash on load without this
|
||||||
|
},
|
||||||
|
|
||||||
|
colorIfServer(a: any): string | undefined {
|
||||||
|
const roleColor = a.author.colorString;
|
||||||
|
|
||||||
|
if (a.channel.guild_id && !settings.store.colorInServers)
|
||||||
|
return roleColor;
|
||||||
|
|
||||||
|
const color = getCustomColorString(a.message.author.id, true);
|
||||||
|
return color ?? roleColor;
|
||||||
|
}
|
||||||
|
});
|
16
src/equicordplugins/customUserColors/styles.css
Normal file
16
src/equicordplugins/customUserColors/styles.css
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.vc-customColors-modal-header {
|
||||||
|
place-content: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-customColors-modal-header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-customColors-modal-content {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-customColors-modal-footer {
|
||||||
|
gap: 16px;
|
||||||
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { makeRange } from "@components/PluginSettings/components";
|
import { makeRange } from "@components/PluginSettings/components";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
@ -24,6 +24,7 @@ import { Logger } from "@utils/Logger";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy } from "@webpack";
|
||||||
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";
|
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";
|
||||||
|
import { getCustomColorString } from "@equicordplugins/customUserColors";
|
||||||
|
|
||||||
const useMessageAuthor = findByCodeLazy('"Result cannot be null because the message is not null"');
|
const useMessageAuthor = findByCodeLazy('"Result cannot be null because the message is not null"');
|
||||||
|
|
||||||
|
@ -164,6 +165,9 @@ export default definePlugin({
|
||||||
|
|
||||||
getColorString(userId: string, channelOrGuildId: string) {
|
getColorString(userId: string, channelOrGuildId: string) {
|
||||||
try {
|
try {
|
||||||
|
if (Settings.plugins.customUserColors.enabled)
|
||||||
|
return getCustomColorString(userId, true);
|
||||||
|
|
||||||
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
|
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
|
||||||
if (guildId == null) return null;
|
if (guildId == null) return null;
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings, Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { openUserProfile } from "@utils/discord";
|
import { openUserProfile } from "@utils/discord";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common";
|
import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
import { getCustomColorString } from "@equicordplugins/customUserColors";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
|
@ -57,6 +58,12 @@ interface Props {
|
||||||
guildId: string;
|
guildId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TypingUserColor(guildId: string, userId: string) {
|
||||||
|
if (!settings.store.showRoleColors) return;
|
||||||
|
if (Settings.plugins.customUserColors.enabled) return getCustomColorString(userId, true);
|
||||||
|
return GuildMemberStore.getMember(guildId, userId)?.colorString;
|
||||||
|
}
|
||||||
|
|
||||||
const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
|
const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
|
||||||
return (
|
return (
|
||||||
<strong
|
<strong
|
||||||
|
@ -68,7 +75,7 @@ const TypingUser = ErrorBoundary.wrap(function ({ user, guildId }: Props) {
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridAutoFlow: "column",
|
gridAutoFlow: "column",
|
||||||
gap: "4px",
|
gap: "4px",
|
||||||
color: settings.store.showRoleColors ? GuildMemberStore.getMember(guildId, user.id)?.colorString : undefined,
|
color: typingUserColor(guildId, user.id),
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue