diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index add75c8a8..23499757e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -77,13 +77,22 @@ jobs: if: steps.check.outputs.release == 'true' run: pnpm buildWebStandalone + - name: Create sources zip + if: steps.check.outputs.release == 'true' + run: zip -r dist/extension-sources.zip . -x "node_modules/*" -x "dist/*" -x ".git/*" + - name: Publish extension if: steps.check.outputs.release == 'true' - run: | - cd dist/browser/chromium-unpacked - pnpx chrome-webstore-upload-cli@2.1.0 upload --auto-publish + run: pnpx publish-browser-extension@latest --chrome-zip dist/extension-chrome.zip --firefox-zip dist/extension-firefox.zip --firefox-sources-zip dist/extension-sources.zip --edge-zip dist/extension-chrome.zip + env: - EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} - CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }} - CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }} - REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }} + CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} + CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }} + CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }} + CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }} + FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }} + FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }} + FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }} + EDGE_PRODUCT_ID: ${{ secrets.EDGE_PRODUCT_ID }} + EDGE_CLIENT_ID: ${{ secrets.EDGE_CLIENT_ID }} + EDGE_API_KEY: ${{ secrets.EDGE_API_KEY }} diff --git a/package.json b/package.json index 386c0b998..09989f17f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "equicord", "private": "true", - "version": "1.13.6.2", + "version": "1.13.6.4", "description": "The other cutest Discord client mod", "homepage": "https://github.com/Equicord/Equicord#readme", "bugs": { diff --git a/packages/vencord-types/package.json b/packages/vencord-types/package.json index 887d7f4ce..d5eb7776f 100644 --- a/packages/vencord-types/package.json +++ b/packages/vencord-types/package.json @@ -1,7 +1,7 @@ { "name": "@equicord/types", "private": false, - "version": "1.13.4", + "version": "1.13.6", "description": "", "types": "index.d.ts", "scripts": { diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 82b6a63d4..9ab0f5cb2 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -17,9 +17,12 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; +import globalBadges from "@equicordplugins/globalBadges"; import BadgeAPIPlugin from "@plugins/_api/badges"; import { ComponentType, HTMLProps } from "react"; +import { isPluginEnabled } from "./PluginManager"; + export const enum BadgePosition { START, END @@ -99,6 +102,18 @@ export function _getBadges(args: BadgeUserArgs) { const donorBadges = BadgeAPIPlugin.getDonorBadges(args.userId); const equicordDonorBadges = BadgeAPIPlugin.getEquicordDonorBadges(args.userId); + const GlobalBadges = isPluginEnabled(globalBadges.name) ? globalBadges.getGlobalBadges(args.userId) : false; + + // do globalbadges first so it shows before the contrib badges but after donor badges + if (GlobalBadges) { + badges.unshift( + ...GlobalBadges.map(badge => ({ + ...args, + ...badge, + })) + ); + } + if (donorBadges) { badges.unshift( ...donorBadges.map(badge => ({ diff --git a/src/api/HeaderBar.tsx b/src/api/HeaderBar.tsx new file mode 100644 index 000000000..04d740f8c --- /dev/null +++ b/src/api/HeaderBar.tsx @@ -0,0 +1,62 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { useEffect, useState } from "@webpack/common"; +import type { JSX } from "react"; + +export type HeaderBarButtonFactory = () => JSX.Element | null; + +interface HeaderBarButton { + render: HeaderBarButtonFactory; + priority: number; +} + +export const buttons = new Map(); + +const listeners = new Set<() => void>(); + +/** + * Add a button to the header bar. + * @param id Unique identifier for the button. + * @param render Function that renders the button component. + * @param priority Higher priority = more to the right. Default is 0. + */ +export function addHeaderBarButton(id: string, render: HeaderBarButtonFactory, priority = 0) { + buttons.set(id, { render, priority }); + listeners.forEach(l => l()); +} + +/** + * Remove a button from the header bar. + * @param id The identifier of the button to remove. + */ +export function removeHeaderBarButton(id: string) { + buttons.delete(id); + listeners.forEach(l => l()); +} + +function HeaderBarButtons() { + const [, forceUpdate] = useState(0); + + useEffect(() => { + const listener = () => forceUpdate(n => n + 1); + listeners.add(listener); + return () => { listeners.delete(listener); }; + }, []); + + const sorted = Array.from(buttons).sort(([, a], [, b]) => a.priority - b.priority); + + return sorted.map(([id, { render: Button }]) => ( + + diff --git a/src/components/ThemeSettings/UserCSSModal.tsx b/src/components/ThemeSettings/UserCSSModal.tsx index c98c3deef..b4f3e4812 100644 --- a/src/components/ThemeSettings/UserCSSModal.tsx +++ b/src/components/ThemeSettings/UserCSSModal.tsx @@ -90,8 +90,8 @@ function ResetButton({ themeSettings, themeId, close, onReset }: ResetButtonProp onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} - onClick={async () => { - await close(); // close the modal first to stop rendering + onClick={() => { + close(); // close the modal first to stop rendering delete themeSettings[themeId]; onReset(); }}> diff --git a/src/components/index.ts b/src/components/index.ts index dd851be5d..a384f977e 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +export * from "./Alert"; export * from "./BaseText"; export * from "./Button"; export * from "./Card"; diff --git a/src/components/settings/DonateButton.tsx b/src/components/settings/DonateButton.tsx index c3e99a83e..88b203325 100644 --- a/src/components/settings/DonateButton.tsx +++ b/src/components/settings/DonateButton.tsx @@ -16,23 +16,24 @@ * along with this program. If not, see . */ +import { Button } from "@components/Button"; import { Heart } from "@components/Heart"; import { openInviteModal } from "@utils/discord"; import { t } from "@utils/translation"; import { ButtonProps } from "@vencord/discord-types"; -import { Button, showToast } from "@webpack/common"; +import { showToast } from "@webpack/common"; export function DonateButton({ - look = Button.Looks.LINK, - color = Button.Colors.TRANSPARENT, - link, + equicord = false, ...props -}: Partial & { link: string; }) { +}: Partial & { equicord?: boolean; }) { + const link = equicord ? "https://github.com/sponsors/thororen1234" : "https://github.com/sponsors/Vendicated"; return ( {!Settings.ignoreResetWarning && ( )} diff --git a/src/components/settings/tabs/sync/CloudTab.tsx b/src/components/settings/tabs/sync/CloudTab.tsx index a01cbee6a..ad77e4bdf 100644 --- a/src/components/settings/tabs/sync/CloudTab.tsx +++ b/src/components/settings/tabs/sync/CloudTab.tsx @@ -19,6 +19,7 @@ import { useSettings } from "@api/Settings"; import { authorizeCloud, deauthorizeCloud } from "@api/SettingsSync/cloudSetup"; import { deleteCloudSettings, eraseAllCloudData, getCloudSettings, putCloudSettings } from "@api/SettingsSync/cloudSync"; +import { Button } from "@components/Button"; import { CheckedTextInput } from "@components/CheckedTextInput"; import { Divider } from "@components/Divider"; import { FormSwitch } from "@components/FormSwitch"; @@ -28,7 +29,7 @@ import { Link } from "@components/Link"; import { Paragraph } from "@components/Paragraph"; import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab"; import { Margins } from "@utils/margins"; -import { Alerts, Button, Tooltip, useState } from "@webpack/common"; +import { Alerts, Tooltip, useState } from "@webpack/common"; function validateUrl(url: string) { try { @@ -60,7 +61,7 @@ function SettingsSyncSection() { />
- - diff --git a/src/components/settings/tabs/updater/Components.tsx b/src/components/settings/tabs/updater/Components.tsx index 8e3757c96..b56419dad 100644 --- a/src/components/settings/tabs/updater/Components.tsx +++ b/src/components/settings/tabs/updater/Components.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { Button } from "@components/Button"; import { Card } from "@components/Card"; import { ErrorCard } from "@components/ErrorCard"; import { Flex } from "@components/Flex"; @@ -13,7 +14,7 @@ import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { relaunch } from "@utils/native"; import { changes, checkForUpdates, update, updateError } from "@utils/updater"; -import { Alerts, Button, React, Toasts, useState } from "@webpack/common"; +import { Alerts, React, Toasts, useState } from "@webpack/common"; import { runWithDispatch } from "./runWithDispatch"; @@ -95,7 +96,7 @@ export function Updatable(props: CommonProps) { {isOutdated && ( )} } ), - start: () => addProfileBadge(Badge), - stop: () => removeProfileBadge(Badge), + async start() { + await loadBadges(); + clearInterval(intervalId); + intervalId = setInterval(loadBadges, 1000 * 60 * 30); + }, + async stop() { + clearInterval(intervalId); + }, + toolboxActions: { + async "Refetch Global Badges"() { + await loadBadges(); + Toasts.show({ + id: Toasts.genId(), + message: "Successfully refetched global badges!", + type: Toasts.Type.SUCCESS + }); + } + }, + get GlobalBadges() { + return GlobalBadges; + }, + getGlobalBadges(userId: string) { + return GlobalBadges[userId]?.map(badge => ({ + iconSrc: badge.badge, + description: badge.tooltip, + position: BadgePosition.START, + props: { + style: { + borderRadius: "50%", + transform: "scale(0.9)" + } + }, + onContextMenu(event, badge) { + ContextMenuApi.openContextMenu(event, () => ); + }, + onClick() { + return openBadgeModal(UserStore.getUser(userId)); + }, + } satisfies ProfileBadge)); + } }); diff --git a/src/equicordplugins/globalBadges/settings.ts b/src/equicordplugins/globalBadges/settings.ts index cfcbf3d1b..85e0db7f0 100644 --- a/src/equicordplugins/globalBadges/settings.ts +++ b/src/equicordplugins/globalBadges/settings.ts @@ -78,12 +78,6 @@ export const settings = definePluginSettings({ default: true, restartNeeded: false }, - showReplugged: { - type: OptionType.BOOLEAN, - description: "Show Replugged Badges", - default: true, - restartNeeded: false - }, apiUrl: { type: OptionType.STRING, description: "API to use", diff --git a/src/equicordplugins/globalBadges/styles.css b/src/equicordplugins/globalBadges/styles.css index 3b098133d..07e07532d 100644 --- a/src/equicordplugins/globalBadges/styles.css +++ b/src/equicordplugins/globalBadges/styles.css @@ -1,15 +1,3 @@ -.vc-global-badges-badges { - align-items: center; - display: flex; - flex-wrap: nowrap; -} - -.vc-global-badges-badge { - width: 20px; - height: 20px; - gap: 1px; -} - .vc-global-badges-modal-name { text-transform: none; flex-grow: 0; diff --git a/src/equicordplugins/globalBadges/types.ts b/src/equicordplugins/globalBadges/types.ts deleted file mode 100644 index fc06b9265..000000000 --- a/src/equicordplugins/globalBadges/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2025 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export type CustomBadge = { - tooltip: string; - badge: string; - custom?: boolean; -}; - -export interface BadgeCache { - badges: { [mod: string]: CustomBadge[]; }; - expires: number; -} diff --git a/src/equicordplugins/globalBadges/utils.ts b/src/equicordplugins/globalBadges/utils.ts index 49e348146..8271166ad 100644 --- a/src/equicordplugins/globalBadges/utils.ts +++ b/src/equicordplugins/globalBadges/utils.ts @@ -4,44 +4,67 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import "./styles.css"; + import { classNameFactory } from "@api/Styles"; import { settings } from "./settings"; -import { BadgeCache } from "./types"; -const cache = new Map(); -const EXPIRES = 1000 * 60 * 15; - -export const cl = classNameFactory("vc-global-badges-"); +export let GlobalBadges = {}; export const INVITE_LINK = "kwHCJPxp8t"; - -export const fetchBadges = (id: string): BadgeCache["badges"] | undefined => { - const cachedValue = cache.get(id); - if (!cache.has(id) || (cachedValue && cachedValue.expires < Date.now())) { - const services: string[] = []; - if (settings.store.showNekocord) services.push("nekocord"); - if (settings.store.showReviewDB) services.push("reviewdb"); - if (settings.store.showAero) services.push("aero"); - if (settings.store.showAliucord) services.push("aliucord"); - if (settings.store.showRa1ncord) services.push("ra1ncord"); - if (settings.store.showVelocity) services.push("velocity"); - if (settings.store.showEnmity) services.push("enmity"); - if (settings.store.showReplugged) services.push("replugged"); - if (settings.store.showCustom) services.push("badgevault"); - - if (services.length === 0) { - cache.set(id, { badges: {}, expires: Date.now() + EXPIRES }); - return {}; - } - - fetch(`${settings.store.apiUrl}${id}?seperated=true&capitalize=true&services=${services.join(",")}`) - .then(res => res.json() as Promise<{ status: number; badges: BadgeCache["badges"]; }>) - .then(body => { - cache.set(id, { badges: body.badges, expires: Date.now() + EXPIRES }); - return body.badges; - }) - .catch(() => null); - } else if (cachedValue) { - return cachedValue.badges; - } +export const cl = classNameFactory("vc-global-badges-"); +export const serviceMap: Record = { + nekocord: "Nekocord", + reviewdb: "ReviewDB", + aero: "Aero", + aliucord: "Aliucord", + ra1ncord: "Ra1ncord", + velocity: "Velocity", + enmity: "Enmity", + badgevault: "BadgeVault", + vencord: "Vencord", + equicord: "Equicord", }; + +export async function loadBadges() { + const url = settings.store.apiUrl.endsWith("/") ? settings.store.apiUrl + "users" : settings.store.apiUrl + "/users"; + const globalBadges = await fetch(url, { cache: "no-cache" }).then(r => r.json()); + const filteredUsers: Record = {}; + + for (const key in globalBadges.users) { + filteredUsers[key] = globalBadges.users[key].filter(b => { + const { mod } = b; + if (!mod) return false; + + const blockedMods = ["vencord", "equicord"]; + if (blockedMods.includes(mod)) return false; + + const conditionalMods = { + aero: settings.store.showAero, + velocity: settings.store.showVelocity, + badgevault: settings.store.showCustom, + nekocord: settings.store.showNekocord, + reviewdb: settings.store.showReviewDB, + aliucord: settings.store.showAliucord, + ra1ncord: settings.store.showRa1ncord, + enmity: settings.store.showEnmity + }; + + if (mod in conditionalMods && !conditionalMods[mod]) return false; + + return true; + }).map(b => { + const modFormatted = serviceMap[b.mod]; + const prefix = settings.store.showPrefix ? `${modFormatted} - ` : ""; + const suffix = settings.store.showSuffix ? ` - ${modFormatted}` : ""; + const tooltip = prefix + b.tooltip + suffix; + return { + ...b, + key: b.tooltip, + tooltip + }; + }); + } + + GlobalBadges = filteredUsers; +} diff --git a/src/equicordplugins/globalNicknames/index.tsx b/src/equicordplugins/globalNicknames/index.tsx deleted file mode 100644 index fdfc4bbf7..000000000 --- a/src/equicordplugins/globalNicknames/index.tsx +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { NavContextMenuPatchCallback } from "@api/ContextMenu"; -import { DataStore } from "@api/index"; -import { definePluginSettings } from "@api/Settings"; -import { Button } from "@components/Button"; -import ErrorBoundary from "@components/ErrorBoundary"; -import { Heading } from "@components/Heading"; -import { EquicordDevs } from "@utils/constants"; -import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; -import { t } from "@utils/translation"; -import definePlugin, { OptionType } from "@utils/types"; -import { Message, User } from "@vencord/discord-types"; -import { findComponentByCodeLazy } from "@webpack"; -import { Menu, React, TextInput, UserStore } from "@webpack/common"; - -const UserBoxIcon = findComponentByCodeLazy("0-3-3H5Zm10 6a3"); - -type NicknamesData = Record; - -let nicknames: NicknamesData = {}; - -const settings = definePluginSettings({ - enableMessages: { - type: OptionType.BOOLEAN, - description: "Show global nicknames in chat messages.", - default: true, - restartNeeded: true - }, - enableMemberList: { - type: OptionType.BOOLEAN, - description: "Show global nicknames in the member list.", - default: true, - restartNeeded: true - }, - enableVoiceChannels: { - type: OptionType.BOOLEAN, - description: "Show global nicknames in voice channels.", - default: true, - restartNeeded: true - }, - enableTypingIndicator: { - type: OptionType.BOOLEAN, - description: "Show global nicknames in the typing indicator.", - default: true, - restartNeeded: true - } -}); - -function NicknameModal({ modalProps, user }: { modalProps: ModalProps; user: User; }) { - const [value, setValue] = React.useState(nicknames[user.id] ?? ""); - - return ( - - - - Change Nickname - - - - -
- - Nickname - - -
-
- - - - -
- ); -} - -const userContextPatch: NavContextMenuPatchCallback = (children, { user }) => { - if (!user) return; - - const currentUser = UserStore.getCurrentUser(); - if (!currentUser || user.id === currentUser.id) return; - - children.push( - , - nicknames[user.id] ? ( - { - delete nicknames[user.id]; - await DataStore.set("GlobalNicknames", nicknames); - }} - /> - ) : ( - openModal(props => ( - - - - ))} - /> - ) - ); -}; - -export default definePlugin({ - name: "GlobalNicknames", - description: "Set custom nicknames for any user globally.", - authors: [EquicordDevs.Prism], - dependencies: ["ContextMenuAPI"], - settings, - - patches: [ - { - find: '="SYSTEM_TAG"', - predicate: () => settings.store.enableMessages, - replacement: { - match: /children:(.+?),"data-text":/, - replace: 'children:$self.patchMessageName(arguments[0])??($1),"data-text":' - } - }, - { - find: "let{colorRoleName:", - predicate: () => settings.store.enableMemberList, - replacement: { - match: /,name:(null!=\i\?\i:\i),colorStrings/, - replace: ",name:$self.patchName($1,arguments[0].user)||($1),colorStrings" - } - }, - { - find: ",connectUserDragSource:", - predicate: () => settings.store.enableVoiceChannels, - replacement: { - match: /nick:(\i),collapsed/, - replace: "nick:$self.patchName($1,arguments[0].user)||$1,collapsed" - } - }, - { - find: "#{intl::THREE_USERS_TYPING}", - predicate: () => settings.store.enableTypingIndicator, - replacement: { - match: /(\i\.\i\.getName\(\i\.guild_id,\i\.id),(\i)\)\)/, - replace: "$1,$self.patchTypingName($2)))" - } - } - ], - - async start() { - const data = await DataStore.get("GlobalNicknames"); - nicknames = data ?? {}; - }, - - patchMessageName(props: { message: Message; }): string | null { - const userId = props?.message?.author?.id; - return userId ? nicknames[userId] ?? null : null; - }, - - patchName(_: string, user: User): string | null { - return nicknames[user.id] ?? null; - }, - - patchTypingName(user: User): User { - const nickname = nicknames[user.id]; - if (!nickname) return user; - return { ...user, globalName: nickname, username: nickname } as User; - }, - - contextMenus: { - "user-context": userContextPatch - } -}); diff --git a/src/equicordplugins/guildTagSettings/index.ts b/src/equicordplugins/guildTagSettings/index.ts index 6a1345b83..65e171c06 100644 --- a/src/equicordplugins/guildTagSettings/index.ts +++ b/src/equicordplugins/guildTagSettings/index.ts @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -import "@equicordplugins/_misc/styles.css"; - import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; diff --git a/src/equicordplugins/holyNotes/index.tsx b/src/equicordplugins/holyNotes/index.tsx index 37d711f34..227c3b2a7 100644 --- a/src/equicordplugins/holyNotes/index.tsx +++ b/src/equicordplugins/holyNotes/index.tsx @@ -20,8 +20,6 @@ import "./style.css"; import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { DataStore } from "@api/index"; -import { removeMessagePopoverButton } from "@api/MessagePopover"; -import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; import { classes } from "@utils/misc"; import { openModal } from "@utils/modal"; @@ -58,15 +56,13 @@ function ToolBarHeader() { const iconClasses = findByProps("iconWrapper", "clickable"); return ( - - Popover(e)} - onClick={() => openModal(props => )} - /> - + openModal(props => )} + /> ); } @@ -75,17 +71,6 @@ export default definePlugin({ name: "HolyNotes", description: "Holy Notes allows you to save messages", authors: [EquicordDevs.Wolfie], - dependencies: ["MessagePopoverAPI", "ChatInputButtonAPI"], - - patches: [ - { - find: ".controlButtonWrapper,", - replacement: { - match: /(function \i\(\i\){)(.{1,200}toolbar.{1,100}mobileToolbar)/, - replace: "$1$self.toolbarAction(arguments[0]);$2" - } - } - ], toolboxActions: { async "Open Notes"() { @@ -97,21 +82,8 @@ export default definePlugin({ "message": messageContextMenuPatch }, - toolbarAction(e) { - if (Array.isArray(e.toolbar)) - return e.toolbar.unshift( - - - - ); + renderHeaderBarButton: ToolBarHeader, - e.toolbar = [ - - - , - e.toolbar, - ]; - }, messagePopoverButton: { icon: NoteButtonPopover, render(message) { @@ -129,8 +101,4 @@ export default definePlugin({ if (await DataStore.keys(HolyNoteStore).then(keys => !keys.includes("Main"))) return noteHandler.newNoteBook("Main"); if (!noteHandlerCache.has("Main")) await DataStoreToCache(); }, - - async stop() { - removeMessagePopoverButton("HolyNotes"); - } }); diff --git a/src/equicordplugins/husk/index.tsx b/src/equicordplugins/husk/index.tsx index 11e3323d5..a42593f32 100644 --- a/src/equicordplugins/husk/index.tsx +++ b/src/equicordplugins/husk/index.tsx @@ -102,7 +102,6 @@ export default definePlugin({ name: "Husk", description: "Adds Husk button (check settings to change used emoji)", authors: [Devs.nin0dev], - dependencies: ["MessagePopoverAPI"], settings, messagePopoverButton: { icon: Husk, diff --git a/src/equicordplugins/iRememberYou/components/ui.tsx b/src/equicordplugins/iRememberYou/components/ui.tsx index 27d530b75..0e3cb03d4 100644 --- a/src/equicordplugins/iRememberYou/components/ui.tsx +++ b/src/equicordplugins/iRememberYou/components/ui.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { BaseText } from "@components/BaseText"; -import { Flex } from "@components/Flex"; +import { HeadingPrimary } from "@components/Heading"; import { Paragraph } from "@components/Paragraph"; import { openUserProfile } from "@utils/discord"; import { Avatar, Clickable, React, TextInput, Tooltip, } from "@webpack/common"; @@ -44,11 +44,9 @@ function UsersCollectionRows({ usersCollection }: { usersCollection: Data["users .map(({ name, users }) => ( @@ -59,14 +57,14 @@ function UsersCollectionRows({ usersCollection }: { usersCollection: Data["users function UserRow({ user, allowOwner = true }: { user: IStorageUser, allowOwner?: boolean; }) { return ( - - - - openUserProfile(user.id)}> - - - - +
+
+ openUserProfile(user.id)}> + + + + +
{props => @@ -74,15 +72,14 @@ function UserRow({ user, allowOwner = true }: { user: IStorageUser, allowOwner?: } - - - - - - {user.id} - - - + + + {user.id} + + +
+
+
); } @@ -94,10 +91,10 @@ function SearchElement({ usersCollection }: { usersCollection: Data["usersCollec
{current && ( - +
{list.filter(user => user.tag.includes(current) || user.username.includes(current)) .map(user => )} - +
)}
); @@ -112,9 +109,9 @@ export function DataUI({ plugin, usersCollection }: { plugin: any; usersCollecti - +
- +
); } diff --git a/src/equicordplugins/iRememberYou/styles.css b/src/equicordplugins/iRememberYou/styles.css index 188f4218a..9eb1a6297 100644 --- a/src/equicordplugins/iRememberYou/styles.css +++ b/src/equicordplugins/iRememberYou/styles.css @@ -5,8 +5,15 @@ flex-direction: column; } +.vc-i-remember-you-header-name { + margin-top: 16px; +} + .vc-i-remember-you-header-btns { - gap: 8px; + display: grid; + grid-template-columns: repeat(2, minmax(0, 350px)); + gap: 10px; + margin-top: 8px; } .vc-i-remember-you-header { @@ -29,49 +36,38 @@ .vc-i-remember-you-user-avatar { cursor: pointer; - margin-right: 30px; + margin-right: 0; } .vc-i-remember-you-user-row { - margin: 0; - width: 100%; - flex-wrap: wrap; - align-items: center; + display: flex; + flex-direction: column; + gap: 6px; + padding: 4px; + align-items: flex-start; } .vc-i-remember-you-user-id { - height: min-content; + align-self: flex-start; opacity: 0.75; + height: min-content; } .vc-i-remember-you-user { - width: 500px; - gap: 250px; + display: flex; + flex-direction: row; align-items: center; - overflow-wrap: break-word; - margin-top: 5px; + gap: 8px; + width: 100%; + margin-top: 0; } .vc-i-remember-you-user-username { margin-top: 2px; } -.vc-i-remember-you-buttons { - margin-top: 10px; -} - -.vc-i-remember-you-buttons-save, -.vc-i-remember-you-buttons-cancel { - margin-left: 490px; - margin-top: 5px; - margin-bottom: 10px; -} - -.vc-i-remember-you-buttons-cancel { - margin-left: 12px; -} - -.vc-i-remember-you-buttons-reset { - cursor: pointer; - margin-left: 12px; +.vc-i-remember-you-user-tooltip { + display: flex; + flex-direction: column; + align-items: flex-start; } \ No newline at end of file diff --git a/src/equicordplugins/ingtoninator/index.tsx b/src/equicordplugins/ingtoninator/index.tsx index 0a777f340..83fc16875 100644 --- a/src/equicordplugins/ingtoninator/index.tsx +++ b/src/equicordplugins/ingtoninator/index.tsx @@ -79,24 +79,39 @@ const IngtoninatorButton: ChatBarButtonFactory = ({ isMainChat }) => { onClick={toggle} > {isEnabled ? ( - - - + enabledIcon() ) : ( - - - + disabledIcon() )} ); }; +function enabledIcon() { + return ( + + + + ); +} + +function disabledIcon() { + return ( + + + + ); +} + export default definePlugin({ name: "Ingtoninator", description: "Suffixes 'ington' to a random word in your message", authors: [EquicordDevs.zyqunix], settings, - renderChatBarButton: IngtoninatorButton, + chatBarButton: { + icon: disabledIcon, + render: IngtoninatorButton + }, start() { addMessagePreSendListener(handleMessage); }, diff --git a/src/equicordplugins/invisibleChat.desktop/index.tsx b/src/equicordplugins/invisibleChat.desktop/index.tsx index 37a14d4d2..4b3235534 100644 --- a/src/equicordplugins/invisibleChat.desktop/index.tsx +++ b/src/equicordplugins/invisibleChat.desktop/index.tsx @@ -65,7 +65,7 @@ function Indicator() { } -const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => { +const ChatBarRender: ChatBarButtonFactory = ({ isMainChat }) => { if (!isMainChat) return null; return ( @@ -76,20 +76,26 @@ const ChatBarIcon: ChatBarButtonFactory = ({ isMainChat }) => { "aria-haspopup": "dialog", }} > - - - + {ChatBarIcon()} ); }; +function ChatBarIcon() { + return ( + + + + ); +} + const settings = definePluginSettings({ savedPasswords: { type: OptionType.STRING, @@ -149,6 +155,11 @@ export default definePlugin({ reporterTestable: ReporterTestable.Patches, settings, + chatBarButton: { + icon: ChatBarIcon, + render: ChatBarRender + }, + patches: [ { // Indicator @@ -165,7 +176,6 @@ export default definePlugin({ const { default: StegCloak } = await getStegCloak(); steggo = new StegCloak(true, false); }, - renderChatBarButton: ChatBarIcon, messagePopoverButton: { icon: PopOverIcon, render(message) { diff --git a/src/equicordplugins/messageFetchTimer/index.tsx b/src/equicordplugins/messageFetchTimer/index.tsx index 40a6a7bff..93640440b 100644 --- a/src/equicordplugins/messageFetchTimer/index.tsx +++ b/src/equicordplugins/messageFetchTimer/index.tsx @@ -72,14 +72,7 @@ const FetchTimeButton: ChatBarButtonFactory = ({ isMainChat }) => { alignItems: "center", gap: "4px" }}> - - - + {FetchTimeIcon()} { ); }; +function FetchTimeIcon() { + return ( + + + + ); +} + function formatTimeAgo(timestamp: Date): string { const now = new Date(); const diff = now.getTime() - timestamp.getTime(); @@ -145,6 +151,11 @@ export default definePlugin({ authors: [EquicordDevs.GroupXyz], settings, + chatBarButton: { + icon: FetchTimeIcon, + render: FetchTimeButton + }, + start() { FluxDispatcher.subscribe("CHANNEL_SELECT", handleChannelSelect); FluxDispatcher.subscribe("LOAD_MESSAGES_SUCCESS", handleMessageLoad); @@ -164,7 +175,5 @@ export default definePlugin({ currentFetch = null; channelTimings.clear(); currentChannelId = null; - }, - - renderChatBarButton: FetchTimeButton, + } }); diff --git a/src/equicordplugins/messageLoggerEnhanced/components/LogsButton.tsx b/src/equicordplugins/messageLoggerEnhanced/components/LogsButton.tsx index 2a12ee8d6..a651396d9 100644 --- a/src/equicordplugins/messageLoggerEnhanced/components/LogsButton.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/components/LogsButton.tsx @@ -21,28 +21,7 @@ import { findComponentByCodeLazy } from "@webpack"; import { openLogModal } from "./LogsModal"; const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); - -export function OpenLogsIcon() { - return ( - - - - - ); -} +const Icon = findComponentByCodeLazy("0-1.27-.97l-2.5.7a3"); export function OpenLogsButton() { return ( @@ -50,7 +29,7 @@ export function OpenLogsButton() { className="vc-log-toolbox-btn" onClick={() => openLogModal()} tooltip={"Open Logs"} - icon={OpenLogsIcon} + icon={Icon} /> ); } diff --git a/src/equicordplugins/messageLoggerEnhanced/index.tsx b/src/equicordplugins/messageLoggerEnhanced/index.tsx index cc30b2403..4c0af5824 100644 --- a/src/equicordplugins/messageLoggerEnhanced/index.tsx +++ b/src/equicordplugins/messageLoggerEnhanced/index.tsx @@ -8,12 +8,11 @@ export const Native = getNative(); import "./styles.css"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { FluxDispatcher, MessageStore, React, UserStore } from "@webpack/common"; +import { FluxDispatcher, MessageStore, UserStore } from "@webpack/common"; import { OpenLogsButton } from "./components/LogsButton"; import { openLogModal } from "./components/LogsModal"; @@ -256,15 +255,6 @@ export default definePlugin({ replace: "deleted:$self.getDeleted(...arguments), editHistory:$self.getEdited(...arguments)," } }, - { - find: ".controlButtonWrapper,", - predicate: () => settings.store.ShowLogsButton, - replacement: { - match: /(function \i\(\i\){)(.{1,200}toolbar.{1,100}mobileToolbar)/, - replace: "$1$self.addIconToToolBar(arguments[0]);$2" - } - }, - { find: "childrenMessageContent:null", replacement: { @@ -310,21 +300,9 @@ export default definePlugin({ openLogModal(); } }, - - addIconToToolBar(e: { toolbar: React.ReactNode[] | React.ReactNode; }) { - if (Array.isArray(e.toolbar)) - return e.toolbar.unshift( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; + renderHeaderBarButton() { + if (!settings.store.ShowLogsButton) return null; + return OpenLogsButton(); }, processMessageFetch, diff --git a/src/equicordplugins/moreStickers/index.tsx b/src/equicordplugins/moreStickers/index.tsx index 51a74910f..c8cf4a8da 100644 --- a/src/equicordplugins/moreStickers/index.tsx +++ b/src/equicordplugins/moreStickers/index.tsx @@ -50,12 +50,12 @@ export default definePlugin({ find: ".gifts)", replacement: [ { - match: /(?<="submit"\)\}\);)(?=.{0,50}null!=(\i))/, - replace: '$1["stickers+"]=3;' + match: /(?<=(,!\i&&\(null==\(\i=\i\.stickers\)\?void 0.*?\i\.push\(\{).{0,15}node:(.{0,45})},"sticker"\)\}\))/, + replace: "$1key:\"stickers+\",node:$2,stickersType:\"stickers+\"},\"stickers+\")})" }, { - match: /(?<=,.{0,5}\(null==\(\i=\i\.stickers\)\?void 0.*?(\i)\.push\(\{.{0,15}node:(.{0,15}{disabled:\i,type:\i)},"sticker"\)\}\))/, - replace: ",arguments[0].type?.submit?.button&&$1.push({key:\"stickers+\",node:$2,stickersType:\"stickers+\"},\"stickers+\")})" + match: /(?<="submit"\)\}\);)(?=.{0,50}null!=(\i))/, + replace: '$1["stickers+"]=3;' } ] }, diff --git a/src/equicordplugins/moreUserTags/index.tsx b/src/equicordplugins/moreUserTags/index.tsx index 93a6b4325..ddf0db266 100644 --- a/src/equicordplugins/moreUserTags/index.tsx +++ b/src/equicordplugins/moreUserTags/index.tsx @@ -35,7 +35,6 @@ export default definePlugin({ name: "MoreUserTags", description: "Adds tags for webhooks and moderative roles (owner, admin, etc.)", authors: [Devs.Cyn, Devs.TheSun, Devs.RyanCaoDev, Devs.LordElias, Devs.AutumnVN, EquicordDevs.Hen], - dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], settings, patches: [ // Make discord actually use our tags diff --git a/src/equicordplugins/neverPausePreviews/index.tsx b/src/equicordplugins/neverPausePreviews/index.tsx index b1bef3d4a..6758e933e 100644 --- a/src/equicordplugins/neverPausePreviews/index.tsx +++ b/src/equicordplugins/neverPausePreviews/index.tsx @@ -16,9 +16,7 @@ * along with this program. If not, see . */ -import "@equicordplugins/_misc/styles.css"; - -import { Paragraph } from "@components/Paragraph"; +import { Alert } from "@components/Alert"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -26,11 +24,11 @@ export default definePlugin({ name: "NeverPausePreviews", description: "Prevents in-call/PiP previews (screenshare, streams, etc) from pausing even if the client loses focus", authors: [EquicordDevs.vappstar], - settingsAboutComponent: () => <> - + settingsAboutComponent: () => ( + This plugin will cause discord to use more resources than normal - - , + + ), patches: [ { find: "streamerPaused()", diff --git a/src/equicordplugins/noAppsAllowed/index.ts b/src/equicordplugins/noAppsAllowed/index.ts new file mode 100644 index 000000000..91e03dd5c --- /dev/null +++ b/src/equicordplugins/noAppsAllowed/index.ts @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "NoAppsAllowed", + description: "returns the bot's tag :skulk:", + authors: [EquicordDevs.meowabyte], + patches: [ + { + find: '"#{intl::APP_TAG::hash}":', + replacement: { + match: /(#{intl::APP_TAG::hash}":\[").*?("\])/, + replace: "$1BOT$2" + } + } + ] +}); diff --git a/src/equicordplugins/noBulletPoints/index.tsx b/src/equicordplugins/noBulletPoints/index.tsx index 21bc3441b..5959a46da 100644 --- a/src/equicordplugins/noBulletPoints/index.tsx +++ b/src/equicordplugins/noBulletPoints/index.tsx @@ -4,23 +4,14 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; export default definePlugin({ name: "NoBulletPoints", description: "Stops you from typing markdown bullet points (stinky)", authors: [Devs.Samwich], - dependencies: ["MessageEventsAPI"], - start() { - this.preSend = addMessagePreSendListener((channelId, msg) => { - msg.content = textProcessing(msg.content); - }); - }, - stop() { - this.preSend = removeMessagePreSendListener((channelId, msg) => { - msg.content = textProcessing(msg.content); - }); + onBeforeMessageSend(channelId, msg) { + msg.content = textProcessing(msg.content); }, }); diff --git a/src/equicordplugins/platformSpoofer/index.tsx b/src/equicordplugins/platformSpoofer/index.tsx index d94b12617..5726fac5b 100644 --- a/src/equicordplugins/platformSpoofer/index.tsx +++ b/src/equicordplugins/platformSpoofer/index.tsx @@ -4,10 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "@equicordplugins/_misc/styles.css"; - import { definePluginSettings } from "@api/Settings"; -import { Paragraph } from "@components/Paragraph"; +import { Alert } from "@components/Alert"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { UserStore } from "@webpack/common"; @@ -51,11 +49,11 @@ export default definePlugin({ name: "PlatformSpoofer", description: "Spoof what platform or device you're on", authors: [EquicordDevs.Drag], - settingsAboutComponent: () => <> - + settingsAboutComponent: () => ( + We can't guarantee this plugin won't get you warned or banned. - - , + + ), settings: settings, patches: [ { diff --git a/src/equicordplugins/polishWording/index.ts b/src/equicordplugins/polishWording/index.ts index 85608ab8c..6334e92ed 100644 --- a/src/equicordplugins/polishWording/index.ts +++ b/src/equicordplugins/polishWording/index.ts @@ -5,9 +5,7 @@ */ import { - addMessagePreSendListener, MessageSendListener, - removeMessagePreSendListener, } from "@api/MessageEvents"; import { definePluginSettings, @@ -67,9 +65,7 @@ export default definePlugin({ name: "PolishWording", description: "Tweaks your messages to make them look nicer and have better grammar. See settings", authors: [Devs.Samwich, EquicordDevs.WKoA], - dependencies: ["MessageEventsAPI"], - start: () => addMessagePreSendListener(presendObject), - stop: () => removeMessagePreSendListener(presendObject), + onBeforeMessageSend: presendObject, settings, }); diff --git a/src/equicordplugins/questCompleter.discordDesktop/index.tsx b/src/equicordplugins/questCompleter.discordDesktop/index.tsx index f4183f41a..5cadf52ce 100644 --- a/src/equicordplugins/questCompleter.discordDesktop/index.tsx +++ b/src/equicordplugins/questCompleter.discordDesktop/index.tsx @@ -16,17 +16,15 @@ * along with this program. If not, see . */ -import "@equicordplugins/_misc/styles.css"; import "./style.css"; import { showNotification } from "@api/Notifications"; import { definePluginSettings } from "@api/Settings"; -import { ErrorBoundary } from "@components/index"; import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { Button, ChannelStore, FluxDispatcher, GuildChannelStore, NavigationRouter, RestAPI, Tooltip, UserStore } from "@webpack/common"; +import { ChannelStore, FluxDispatcher, GuildChannelStore, NavigationRouter, RestAPI, UserStore } from "@webpack/common"; const QuestIcon = findComponentByCodeLazy("10.47a.76.76"); const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"'); @@ -38,16 +36,13 @@ let questIdCheck = 0; function ToolBarHeader() { return ( - - - - + ); } @@ -213,11 +208,6 @@ async function openCompleteQuestUI() { } const settings = definePluginSettings({ - useNavBar: { - description: "Move quest button down to the server nav bar", - type: OptionType.BOOLEAN, - default: false, - }, disableNotifications: { description: "Disable notifications when no quests are available or when a quest is completed", type: OptionType.BOOLEAN, @@ -238,22 +228,6 @@ export default definePlugin({ replace: "onClick:()=>$self.mobileQuestPatch($1)" }, }, - { - find: '?"BACK_FORWARD_NAVIGATION":', - replacement: { - match: /"HELP".{0,100}className:\i\}\)(?=\])/, - replace: "$&,$self.renderTitleBar()" - }, - predicate: () => !settings.store.useNavBar - }, - { - find: ".controlButtonWrapper,", - replacement: { - match: /(function \i\(\i\){)(.{1,200}toolbar.{1,200}mobileToolbar)/, - replace: "$1$self.renderNavBar(arguments[0]);$2" - }, - predicate: () => settings.store.useNavBar - } ], mobileQuestPatch(questId) { if (questId === questIdCheck) return; @@ -265,37 +239,5 @@ export default definePlugin({ } }); }, - renderTitleBar() { - return ( - - - {tooltipProps => ( - - )} - - - ); - }, - renderNavBar(e) { - if (Array.isArray(e.toolbar)) - return e.toolbar.unshift( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; - } + renderHeaderBarButton: ToolBarHeader }); diff --git a/src/equicordplugins/questFocused/index.tsx b/src/equicordplugins/questFocused/index.tsx index c8469c9b9..695a4f023 100644 --- a/src/equicordplugins/questFocused/index.tsx +++ b/src/equicordplugins/questFocused/index.tsx @@ -4,20 +4,18 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "@equicordplugins/_misc/styles.css"; - -import { Paragraph } from "@components/Paragraph"; +import { Alert } from "@components/Alert"; import { EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; export default definePlugin({ name: "QuestFocused", description: "Prevent the quests player from pausing and possibly skip it all together.", - settingsAboutComponent: () => <> - + settingsAboutComponent: () => ( + You might need to spam left mouse button on the video to skip it. - - , + + ), authors: [EquicordDevs.secp192k1], patches: [ // Block pausing diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index 0d6216c61..bf6af35d4 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -1073,7 +1073,7 @@ export default definePlugin({ }, { // Adds a maxDigits prop to the LowerBadge component which allows for not truncating, or for truncating at a specific threshold. - find: "STATUS_DANGER,disableColor", + find: '"renderBadgeCount"])', group: true, replacement: [ { @@ -1251,7 +1251,7 @@ export default definePlugin({ }, { // Add the trigger to the memo for rerendering the progress label. - match: /(\i.intl.string\(\i.\i#{intl::QUESTS_SEE_CODE}\)}\)}},\[)/, + match: /(\i\.intl\.string\(\i\.\i#{intl::QUESTS_SEE_CODE}\)\}\)\}\},\[|\)\}\}\},\[)/, replace: "$1questRerenderTrigger," }, { diff --git a/src/equicordplugins/questify/utils/components.tsx b/src/equicordplugins/questify/utils/components.tsx index 9b14f999a..ff61c28ef 100644 --- a/src/equicordplugins/questify/utils/components.tsx +++ b/src/equicordplugins/questify/utils/components.tsx @@ -14,8 +14,8 @@ import { formatLowerBadge } from "./misc"; // GuildlessServerListItem's built-in pill does not support unread state. export const GuildlessServerListItemComponent = findComponentByCodeLazy("tooltip:", "lowerBadgeSize:"); export const GuildedServerListItemPillComponent = findComponentByCodeLazy('"pill":"empty"'); -export const ServerListItemLowerBadgeComponent = findComponentByCodeLazy("STATUS_DANGER,disableColor:"); -export const ServerListItemUpperBadgeComponent = findComponentByCodeLazy("STATUS_DANGER,shape:", "iconBadge,"); +export const ServerListItemLowerBadgeComponent = findComponentByCodeLazy("numberBadge", '"renderBadgeCount"'); +export const ServerListItemUpperBadgeComponent = findComponentByCodeLazy("iconBadge", ".ROUND,disableColor"); export const RadioGroup = findComponentByCodeLazy(',["label","description"'); export const QuestTile = findComponentByCodeLazy(".rowIndex,trackGuildAndChannelMetadata"); diff --git a/src/equicordplugins/sekaiStickers/index.tsx b/src/equicordplugins/sekaiStickers/index.tsx index bfbc07feb..af1d6c1af 100644 --- a/src/equicordplugins/sekaiStickers/index.tsx +++ b/src/equicordplugins/sekaiStickers/index.tsx @@ -34,9 +34,11 @@ export default definePlugin({ name: "SekaiStickers", description: "Sekai Stickers built in discord originally from github.com/TheOriginalAyaka", authors: [Devs.MaiKokain], - dependencies: ["ChatInputButtonAPI"], settings, - renderChatBarButton: SekaiStickerChatButton, + chatBarButton: { + icon: kanadeSvg, + render: SekaiStickerChatButton + }, async start() { const fonts = [{ name: "YurukaStd", url: "https://raw.githubusercontent.com/TheOriginalAyaka/sekai-stickers/47a2ca33b8cb35f59800e8faad48980e4ce5ea71/src/fonts/YurukaStd.woff2" }, { name: "SSFangTangTi", url: "https://raw.githubusercontent.com/TheOriginalAyaka/sekai-stickers/main/src/fonts/ShangShouFangTangTi.woff2" }]; if (!IS_FONTS_LOADED) { diff --git a/src/equicordplugins/showBadgesInChat/index.tsx b/src/equicordplugins/showBadgesInChat/index.tsx index ec23fde67..4d672e144 100644 --- a/src/equicordplugins/showBadgesInChat/index.tsx +++ b/src/equicordplugins/showBadgesInChat/index.tsx @@ -4,7 +4,6 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; import { Devs, EquicordDevs } from "@utils/constants"; import { isEquicordPluginDev, isPluginDev } from "@utils/misc"; import definePlugin from "@utils/types"; @@ -142,12 +141,8 @@ export default definePlugin({ name: "ShowBadgesInChat", authors: [Devs.Inbestigator, EquicordDevs.KrystalSkull], description: "Shows the message author's badges beside their name in chat.", - dependencies: ["MessageDecorationsAPI"], settings, - start: () => { - addMessageDecoration("vc-show-badges-in-chat", props => props.message?.author ? : null); - }, - stop: () => { - removeMessageDecoration("vc-show-badges-in-chat"); + renderMessageDecoration(props) { + return props.message?.author ? : null; } }); diff --git a/src/equicordplugins/showResourceChannels/index.ts b/src/equicordplugins/showResourceChannels/index.ts index fbba862ee..d3ca76589 100644 --- a/src/equicordplugins/showResourceChannels/index.ts +++ b/src/equicordplugins/showResourceChannels/index.ts @@ -11,11 +11,15 @@ export default definePlugin({ name: "ShowResourceChannels", description: "shows the channels hidden behind the server resources in the channel list", authors: [EquicordDevs.VillainsRule], - patches: [{ - find: '"should_show_in_recents"', - replacement: [{ - match: /\i\.hideResourceChannels&&/, - replace: "false&&" - }] - }] + patches: [ + { + find: ".GUILD_DIRECTORY:null", + replacement: [ + { + match: /\i\.hideResourceChannels&&/, + replace: "false&&" + } + ] + } + ] }); diff --git a/src/equicordplugins/signature/index.tsx b/src/equicordplugins/signature/index.tsx index 69d428d8f..7c5630650 100644 --- a/src/equicordplugins/signature/index.tsx +++ b/src/equicordplugins/signature/index.tsx @@ -7,7 +7,6 @@ import { ChatBarButton, ChatBarButtonFactory } from "@api/ChatButtons"; import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; -import { addMessagePreSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; import { definePluginSettings } from "@api/Settings"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -70,13 +69,13 @@ const SignatureToggle: ChatBarButtonFactory = ({ isMainChat }) => { ); }; -const handleMessage = ((channelId, msg) => { - if (!settings.store.isEnabled) { - msg.content = msg.content; - } else { - msg.content = textProcessing(msg.content); - } -}); +function SignatureIcon() { + return ( + + + + ); +} const ChatBarContextCheckbox: NavContextMenuPatchCallback = children => { const { isEnabled, contextMenu } = settings.use(["isEnabled", "contextMenu"]); @@ -102,17 +101,23 @@ export default definePlugin({ name: "Signature", description: "Automated fingerprint/end text", authors: [Devs.Ven, Devs.Rini, Devs.ImBanana, EquicordDevs.KrystalSkull], - dependencies: ["MessageEventsAPI", "ChatInputButtonAPI"], - renderChatBarButton: SignatureToggle, - + onBeforeMessageSend(channelId, msg) { + if (!settings.store.isEnabled) { + msg.content = msg.content; + } else { + msg.content = textProcessing(msg.content); + } + }, start: () => { if (settings.store.isEnabled) true; - addMessagePreSendListener(handleMessage); }, stop: () => { if (settings.store.isEnabled) false; - removeMessagePreSendListener(handleMessage); + }, + chatBarButton: { + icon: SignatureIcon, + render: SignatureToggle }, settings, diff --git a/src/equicordplugins/soggy/index.tsx b/src/equicordplugins/soggy/index.tsx index edd0361e2..caf1b4659 100644 --- a/src/equicordplugins/soggy/index.tsx +++ b/src/equicordplugins/soggy/index.tsx @@ -6,7 +6,6 @@ import { AudioPlayerInterface, createAudioPlayer } from "@api/AudioPlayer"; import { definePluginSettings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; import { EquicordDevs } from "@utils/constants"; import { ModalProps, ModalRoot, openModal } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; @@ -143,15 +142,7 @@ export default definePlugin({ authors: [EquicordDevs.sliwka], settings, dependencies: ["AudioPlayerAPI"], - patches: [ - { - find: ".controlButtonWrapper,", - replacement: { - match: /(function \i\(\i\){)(.{1,200}toolbar.{1,450}mobileToolbar)/, - replace: "$1$self.addIconToToolBar(arguments[0]);$2" - } - } - ], + renderHeaderBarButton: SoggyButton, start() { assignBoop(settings.store.boopLink, settings.store.boopVolume * 100); @@ -162,21 +153,4 @@ export default definePlugin({ boopSound?.delete(); song?.delete(); }, - - // taken from message logger lol - addIconToToolBar(e: { toolbar: React.ReactNode[] | React.ReactNode; }) { - if (Array.isArray(e.toolbar)) - return e.toolbar.unshift( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; - }, }); diff --git a/src/equicordplugins/soundBoardLogger/index.tsx b/src/equicordplugins/soundBoardLogger/index.tsx index 41aa76180..5d95a2f2d 100644 --- a/src/equicordplugins/soundBoardLogger/index.tsx +++ b/src/equicordplugins/soundBoardLogger/index.tsx @@ -4,14 +4,11 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { addChatBarButton, removeChatBarButton } from "@api/ChatButtons"; import { disableStyle, enableStyle } from "@api/Styles"; -import ErrorBoundary from "@components/ErrorBoundary"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; -import { CharBarButton, ChatBarIcon } from "./components/Icons"; import { OpenSBLogsButton } from "./components/SoundBoardLog"; import settings from "./settings"; import { updateLoggedSounds } from "./store"; @@ -22,33 +19,9 @@ export default definePlugin({ name: "SoundBoardLogger", authors: [Devs.Moxxie, EquicordDevs.Fres, Devs.amy, Devs.thororen], description: "Logs all soundboards that are played in a voice chat and allows you to download them", - dependencies: ["AudioPlayerAPI", "ChatInputButtonAPI"], - patches: [ - { - predicate: () => settings.store.IconLocation === "toolbar", - find: ".controlButtonWrapper,", - replacement: { - match: /(function \i\(\i\){)(.{1,500}toolbar.{1,500}mobileToolbar)/, - replace: "$1$self.addSBIconToToolBar(arguments[0]);$2" - } - } - ], + dependencies: ["AudioPlayerAPI"], settings, - addSBIconToToolBar(e: { toolbar: React.ReactNode[] | React.ReactNode; }) { - if (Array.isArray(e.toolbar)) - return e.toolbar.unshift( - - - - ); - - e.toolbar = [ - - - , - e.toolbar, - ]; - }, + renderHeaderBarButton: OpenSBLogsButton, start() { enableStyle(styles); FluxDispatcher.subscribe("VOICE_CHANNEL_EFFECT_SEND", async sound => { @@ -56,10 +29,8 @@ export default definePlugin({ await updateLoggedSounds(sound); getListeners().forEach(cb => cb()); }); - if (settings.store.IconLocation === "chat") addChatBarButton("vc-soundlog-button", CharBarButton, ChatBarIcon); }, stop() { disableStyle(styles); - if (settings.store.IconLocation === "chat") removeChatBarButton("vc-soundlog-button"); } }); diff --git a/src/equicordplugins/soundBoardLogger/settings.tsx b/src/equicordplugins/soundBoardLogger/settings.tsx index 6c9453077..75d211c5d 100644 --- a/src/equicordplugins/soundBoardLogger/settings.tsx +++ b/src/equicordplugins/soundBoardLogger/settings.tsx @@ -85,15 +85,6 @@ const settings = definePluginSettings({ default: 0.5, markers: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] }, - IconLocation: { - description: "choose where to show the SoundBoard Log icon (requires restart)", - type: OptionType.SELECT, - options: [ - { label: "Toolbar", value: "toolbar", default: true }, - { label: "Chat input", value: "chat" } - ], - restartNeeded: true - }, }); export default settings; diff --git a/src/equicordplugins/splitLargeMessages/index.ts b/src/equicordplugins/splitLargeMessages/index.ts index 50dadff56..0e52af7f4 100644 --- a/src/equicordplugins/splitLargeMessages/index.ts +++ b/src/equicordplugins/splitLargeMessages/index.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents"; +import { MessageSendListener } from "@api/MessageEvents"; import { definePluginSettings } from "@api/Settings"; import { EquicordDevs } from "@utils/constants"; import { getCurrentChannel, sendMessage } from "@utils/discord"; @@ -117,16 +117,11 @@ export default definePlugin({ name: "SplitLargeMessages", description: "Splits large messages into multiple to fit Discord's message limit.", authors: [EquicordDevs.Reycko], - dependencies: ["MessageEventsAPI"], settings, + onBeforeMessageSend: listener, start() { if (settings.store.maxLength === 0) maxLength = autoMaxLength(); - addMessagePreSendListener(listener); - }, - - stop() { - removeMessagePreSendListener(listener); }, patches: [ diff --git a/src/equicordplugins/talkInReverse/index.tsx b/src/equicordplugins/talkInReverse/index.tsx index 7ecf97e5a..c469c9b31 100644 --- a/src/equicordplugins/talkInReverse/index.tsx +++ b/src/equicordplugins/talkInReverse/index.tsx @@ -54,10 +54,21 @@ const ReverseMessageToggle: ChatBarButtonFactory = ({ isMainChat }) => { ); }; +function ReverseMessageIcon() { + return ( + + + + ); +} + export default definePlugin({ name: "TalkInReverse", authors: [EquicordDevs.Tolgchu], description: "Reverses the message content before sending it.", - dependencies: ["MessageEventsAPI", "ChatInputButtonAPI"], - renderChatBarButton: ReverseMessageToggle, + dependencies: ["MessageEventsAPI"], + chatBarButton: { + icon: ReverseMessageIcon, + render: ReverseMessageToggle + } }); diff --git a/src/equicordplugins/translatePlus/index.tsx b/src/equicordplugins/translatePlus/index.tsx index 2457d0877..78e48b120 100644 --- a/src/equicordplugins/translatePlus/index.tsx +++ b/src/equicordplugins/translatePlus/index.tsx @@ -46,7 +46,6 @@ const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => export default definePlugin({ name: "Translate+", description: "Vencord's translate plugin but with support for artistic languages!", - dependencies: ["MessageAccessoriesAPI"], authors: [Devs.Ven, EquicordDevs.Prince527], settings, contextMenus: { diff --git a/src/equicordplugins/universalMention/index.tsx b/src/equicordplugins/universalMention/index.tsx index 9ee127883..4b3c75f20 100644 --- a/src/equicordplugins/universalMention/index.tsx +++ b/src/equicordplugins/universalMention/index.tsx @@ -5,7 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; -import { Paragraph } from "@components/Paragraph"; +import { Alert } from "@components/Alert"; import { EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; @@ -41,9 +41,9 @@ export default definePlugin({ description: "Mention any user, regardless of channel access.", settings, settingsAboutComponent: () => ( - + Using Global Mention can cause performance issues and show an absurd amount of users in the autocomplete. - + ), patches: [ diff --git a/src/equicordplugins/unreadBadgeCount/index.tsx b/src/equicordplugins/unreadBadgeCount/index.tsx index c28dd7139..e1ee1d4e9 100644 --- a/src/equicordplugins/unreadBadgeCount/index.tsx +++ b/src/equicordplugins/unreadBadgeCount/index.tsx @@ -14,7 +14,7 @@ import { ReadStateStore, useStateFromStores } from "@webpack/common"; const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore"); const JoinedThreadsStore = findStoreLazy("JoinedThreadsStore"); -const NumberBadge = findComponentByCodeLazy("numberBadge", ".STATUS_DANGER,"); +const NumberBadge = findComponentByCodeLazy("numberBadge", '"renderBadgeCount"'); const settings = definePluginSettings({ showOnMutedChannels: { diff --git a/src/equicordplugins/videoSpeed/index.tsx b/src/equicordplugins/videoSpeed/index.tsx deleted file mode 100644 index 448d9ae71..000000000 --- a/src/equicordplugins/videoSpeed/index.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { definePluginSettings } from "@api/Settings"; -import ErrorBoundary from "@components/ErrorBoundary"; -import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; -import { Tooltip, useRef, useState } from "@webpack/common"; - -const settings = definePluginSettings( - { - preservePitch: { - type: OptionType.BOOLEAN, - description: "Should pitch be preserved when changing speed?", - default: false, - restartNeeded: true - }, - }); - -export default definePlugin({ - name: "VideoSpeed", - description: "Allows you to change the speed of videos", - authors: [Devs.Samwich], - settings, - patches: [ - { - find: /\.VIDEO\?\i\.videoControls:/, - replacement: { - match: /children:\[this\.renderPlayIcon\(\),.{0,200}\.setDurationRef}\),/, - replace: "$&$self.SpeedButton()," - }, - }, - ], - SpeedButton: ErrorBoundary.wrap(() => { - const elementRef = useRef(null); - - const [speed, setSpeed] = useState(1); - - const speedPaths = - { - 1.5: "M320 48a48 48 0 1 1 96 0a48 48 0 1 1-96 0m-115.5 73.3c-5.4-2.5-11.7-1.9-16.4 1.7l-40.9 30.7c-14.1 10.6-34.2 7.7-44.8-6.4s-7.7-34.2 6.4-44.8l40.9-30.7c23.7-17.8 55.3-21 82.1-8.4l90.4 42.5c29.1 13.7 36.8 51.6 15.2 75.5L299.1 224h97.4c30.3 0 53 27.7 47.1 57.4l-28.2 140.9c-3.5 17.3-20.3 28.6-37.7 25.1s-28.6-20.3-25.1-37.7L377 288h-70.3c8.6 19.6 13.3 41.2 13.3 64c0 88.4-71.6 160-160 160S0 440.4 0 352s71.6-160 160-160c11.1 0 22 1.1 32.4 3.3l54.2-54.2zM160 448a96 96 0 1 0 0-192a96 96 0 1 0 0 192", - 1.25: "M320 48a48 48 0 1 0-96 0a48 48 0 1 0 96 0M125.7 175.5c9.9-9.9 23.4-15.5 37.5-15.5c1.9 0 3.8.1 5.6.3L137.6 254c-9.3 28 1.7 58.8 26.8 74.5l86.2 53.9l-25.4 88.8c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l28.7-100.4c5.9-20.6-2.6-42.6-20.7-53.9L238 299l30.9-82.4l5.1 12.3c15 35.8 49.9 59.1 88.7 59.1H384c17.7 0 32-14.3 32-32s-14.3-32-32-32h-21.3c-12.9 0-24.6-7.8-29.5-19.7l-6.3-15c-14.6-35.1-44.1-61.9-80.5-73.1l-48.7-15C186.6 97.8 175 96 163.3 96c-31 0-60.8 12.3-82.7 34.3l-23.2 23.1c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l23.1-23.1zM91.2 352H32c-17.7 0-32 14.3-32 32s14.3 32 32 32h69.6c19 0 36.2-11.2 43.9-28.5l11.5-25.9l-9.5-6a95.394 95.394 0 0 1-37.9-44.9z", - 1: "M112 48a48 48 0 1 1 96 0a48 48 0 1 1-96 0m40 304v128c0 17.7-14.3 32-32 32s-32-14.3-32-32V256.9l-28.6 47.6c-9.1 15.1-28.8 20-43.9 10.9s-20-28.8-10.9-43.9l58.3-97c17.4-28.9 48.6-46.6 82.3-46.6h29.7c33.7 0 64.9 17.7 82.3 46.6l58.3 97c9.1 15.1 4.2 34.8-10.9 43.9s-34.8 4.2-43.9-10.9L232 256.9V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V352z", - 0.5: "M368 32c41.7 0 75.9 31.8 79.7 72.5l85.6 26.3c25.4 7.8 42.8 31.3 42.8 57.9c0 21.8-11.7 41.9-30.7 52.7l-144.6 82.1l92.5 92.5H544c17.7 0 32 14.3 32 32s-14.3 32-32 32h-64c-8.5 0-16.6-3.4-22.6-9.4L346.9 360.2c11.7-36 3.2-77.1-25.4-105.7c-40.6-40.6-106.3-40.6-146.9-.1l-73.6 70c-6.4 6.1-6.7 16.2-.6 22.6s16.2 6.6 22.6.6l73.8-70.2l.1-.1l.1-.1c3.5-3.5 7.3-6.6 11.3-9.2c27.9-18.5 65.9-15.4 90.5 9.2c24.7 24.7 27.7 62.9 9 90.9c-2.6 3.8-5.6 7.5-9 10.9l-37 37H352c17.7 0 32 14.3 32 32s-14.3 32-32 32H64c-35.3 0-64-28.7-64-64C0 249.6 127 112.9 289.3 97.5C296.2 60.2 328.8 32 368 32m0 104a24 24 0 1 0 0-48a24 24 0 1 0 0 48" - }; - - return
- - {tooltipProps => ( -
{ - console.log(e); - let newSpeed; - switch (speed) { - case 1: - newSpeed = 1.25; - break; - case 1.25: - newSpeed = 1.5; - break; - case 1.5: - newSpeed = 0.5; - break; - case 0.5: - newSpeed = 1; - break; - } - const parent = e.currentTarget.parentNode!.parentNode!.parentNode!; - - // this works with audio too as it is but it doesnt always select the right audio tag - const media = parent.querySelector("video")! /* ?? document.querySelector("source")?.parentElement*/; - console.log(media); - media.playbackRate = newSpeed; - media.preservesPitch = settings.store.preservePitch; - setSpeed(newSpeed); - }} - > - -
- ) - } -
-
; - }, { noop: true }) -}); diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index 873bf6705..354eae6d1 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -100,7 +100,7 @@ async function loadAllBadges(noCache = false) { let intervalId: any; -function BadgeContextMenu({ badge }: { badge: ProfileBadge & BadgeUserArgs; }) { +export function BadgeContextMenu({ badge }: { badge: ProfileBadge & BadgeUserArgs; }) { return ( - + @@ -113,7 +113,7 @@ export function EquicordDonorModal() { - + diff --git a/src/plugins/_api/headerBar.ts b/src/plugins/_api/headerBar.ts new file mode 100644 index 000000000..20af4ecb4 --- /dev/null +++ b/src/plugins/_api/headerBar.ts @@ -0,0 +1,24 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { EquicordDevs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "HeaderBarAPI", + description: "API to add buttons to the header bar.", + authors: [EquicordDevs.Prism], + + patches: [ + { + find: '?"BACK_FORWARD_NAVIGATION":', + replacement: { + match: /(?<=\i\.Fragment,\{children:\[.{1,500}className:\i\}\))(?=\])/, + replace: ",...Vencord.Api.HeaderBar._addButtons()" + } + } + ] +}); diff --git a/src/plugins/accountPanelServerProfile/index.tsx b/src/plugins/accountPanelServerProfile/index.tsx index 57144ec08..cbf23bbab 100644 --- a/src/plugins/accountPanelServerProfile/index.tsx +++ b/src/plugins/accountPanelServerProfile/index.tsx @@ -4,14 +4,16 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { isPluginEnabled } from "@api/PluginManager"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; +import alwaysExpandProfiles from "@equicordplugins/alwaysExpandProfiles"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { User } from "@vencord/discord-types"; import { findComponentByCodeLazy } from "@webpack"; -import { ContextMenuApi, Menu } from "@webpack/common"; +import { ContextMenuApi, Menu, UserStore } from "@webpack/common"; interface UserProfileProps { popoutProps: Record; @@ -37,6 +39,10 @@ const AccountPanelContextMenu = ErrorBoundary.wrap(() => { label={prioritizeServerProfile ? "View Account Profile" : "View Server Profile"} disabled={getCurrentChannel()?.getGuildId() == null} action={e => { + if (isPluginEnabled(alwaysExpandProfiles.name)) { + const user = UserStore.getCurrentUser(); + return alwaysExpandProfiles.openUserModal(user); + } openAlternatePopout = true; accountPanelRef.current?.click(); }} diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index cfa7193f9..dc8b1ce9d 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -91,21 +91,24 @@ export default definePlugin({ replacement: [ // Use Decor avatar decoration hook { - match: /(?<=\i\)\({avatarDecoration:)(\i)(?=,)(?<=currentUser:(\i).+?)/, + match: /(?<=\i\)\({avatarDecoration:)\i(?=,)(?<=currentUser:(\i).+?)/, replace: "$self.useUserDecorAvatarDecoration($1)??$&" } ] }, - // Messages - { - find: '"Message Username"', + ...[ + '"Message Username"', // Messages + ".nameplatePreview,{", // Nameplate preview + "#{intl::ayozFl::raw}", // Avatar preview + ].map(find => ({ + find, replacement: [ { match: /(?<=userValue.{0,25}void 0:)((\i)\.avatarDecoration)/, replace: "$self.useUserDecorAvatarDecoration($2)??$1" } ] - }, + })), ], settings, diff --git a/src/plugins/decor/ui/index.ts b/src/plugins/decor/ui/index.ts index ec9a9e74f..62040508f 100644 --- a/src/plugins/decor/ui/index.ts +++ b/src/plugins/decor/ui/index.ts @@ -10,5 +10,5 @@ import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack"; export const cl = classNameFactory("vc-decor-"); export const DecorationModalStyles = findByPropsLazy("modalPreview", "modalCloseButton", "spinner", "modal"); -export const requireAvatarDecorationModal = extractAndLoadChunksLazy(["initialSelectedDecoration:", /initialSelectedDecoration:\i,.{0,300}Promise\.all/]); +export const requireAvatarDecorationModal = extractAndLoadChunksLazy(["initialSelectedDecoration:", /initialSelectedDecoration:\i,.{0,300}\i\.e\(/]); export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]); diff --git a/src/plugins/noUnblockToJump/index.ts b/src/plugins/noUnblockToJump/index.ts index b36a2e24d..5d876fc7b 100644 --- a/src/plugins/noUnblockToJump/index.ts +++ b/src/plugins/noUnblockToJump/index.ts @@ -27,7 +27,7 @@ export default definePlugin({ { find: "#{intl::UNIGNORE_TO_JUMP_BODY}", replacement: { - match: /return \i\.\i\.isBlockedForMessage\(/, + match: /if\(\i\.\i\.isBlockedForMessage\(/, replace: "return true;$&" } } diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 9d1541dbc..17d44cd45 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -18,9 +18,6 @@ import "./style.css"; -import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; -import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; -import { addNicknameIcon, removeNicknameIcon } from "@api/NicknameIcons"; import { definePluginSettings, migratePluginSetting, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; @@ -192,49 +189,22 @@ const PlatformIndicator = ({ user, isProfile, isMessage, isMemberList }: Platfor ); }; -function toggleMemberListDecorators(enabled: boolean) { - if (enabled) { - addMemberListDecorator("PlatformIndicators", props => ); - } else { - removeMemberListDecorator("PlatformIndicators"); - } -} - -function toggleNicknameIcons(enabled: boolean) { - if (enabled) { - addNicknameIcon("PlatformIndicators", props => , 1); - } else { - removeNicknameIcon("PlatformIndicators"); - } -} - -function toggleMessageDecorators(enabled: boolean) { - if (enabled) { - addMessageDecoration("PlatformIndicators", props => ); - } else { - removeMessageDecoration("PlatformIndicators"); - } -} - migratePluginSetting("PlatformIndicators", "profiles", "badges"); const settings = definePluginSettings({ list: { type: OptionType.BOOLEAN, description: "Show indicators in the member list", default: true, - onChange: toggleMemberListDecorators }, profiles: { type: OptionType.BOOLEAN, description: "Show indicators in user profiles", default: true, - onChange: toggleNicknameIcons }, messages: { type: OptionType.BOOLEAN, description: "Show indicators inside messages", default: true, - onChange: toggleMessageDecorators }, colorMobileIndicator: { type: OptionType.BOOLEAN, @@ -278,19 +248,21 @@ export default definePlugin({ name: "PlatformIndicators", description: "Adds platform indicators (Desktop, Mobile, Web...) to users", authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven], - dependencies: ["MemberListDecoratorsAPI", "NicknameIconsAPI", "MessageDecorationsAPI"], settings, - - start() { - if (settings.store.list) toggleMemberListDecorators(true); - if (settings.store.profiles) toggleNicknameIcons(true); - if (settings.store.messages) toggleMessageDecorators(true); + renderNicknameIcon(props) { + if (!settings.store.profiles) return null; + return ( + + ); }, + renderMemberListDecorator(props) { + if (!settings.store.list) return null; + return ; - stop() { - if (settings.store.list) toggleMemberListDecorators(false); - if (settings.store.profiles) toggleNicknameIcons; - if (settings.store.messages) toggleMessageDecorators(false); + }, + renderMessageDecoration(props) { + if (!settings.store.messages) return null; + return ; }, patches: [ diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 6f6234525..9b747cb5b 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -4,6 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { DataStore } from "@api/index"; import "./style.css"; import { definePluginSettings, Settings } from "@api/Settings"; @@ -11,15 +12,21 @@ import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { GuildMember, Message, User } from "@vencord/discord-types"; import { findByCodeLazy, findStoreLazy } from "@webpack"; -import { ChannelStore, GuildMemberStore, GuildStore, MessageStore, RelationshipStore, StreamerModeStore, useEffect, UserStore } from "@webpack/common"; +import { ChannelStore, GuildMemberStore, GuildStore, Menu, MessageStore, RelationshipStore, StreamerModeStore, TextInput, useEffect, UserStore, useState } from "@webpack/common"; import { JSX } from "react"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { Button, ErrorBoundary, Heading, TextButton } from "@components/index"; +import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/index"; const wrapEmojis = findByCodeLazy("lastIndex;return"); const AccessibilityStore = findStoreLazy("AccessibilityStore"); const colorPattern = /^#(?:[\da-f]{3}){1,2}$|^#(?:[\da-f]{4}){1,2}$|(rgb|hsl)a?\((\s*-?\d+%?\s*,){2}(\s*-?\d+%?\s*)\)|(rgb|hsl)a?\((\s*-?\d+%?\s*,){3}\s*(0|(0?\.\d+)|1)\)$/iu; const roleColorPattern = /^role((?:\+|-)\d{0,4})?$/iu; const symbolPattern = /^[\p{S}\p{P}]{1,3}$/iu; -const templatePattern = /(?:\{(?:friend|nick|display|user)(?:,\s*(?:friend|nick|display|user))*\})/iu; +const templatePattern = /(?:\{(?:custom|friend|nick|display|user)(?:,\s*(?:custom|friend|nick|display|user))*\})/iu; + +type CustomNicknameData = Record; +let customNicknames: CustomNicknameData = {}; function adjustHex(color: string, percent: number): string { let hex = color.replace("#", ""); @@ -208,8 +215,8 @@ function validTemplate(value: string) { suffix.length > 3 || (!!prefix && !symbolPattern.test(prefix)) || (!!suffix && !symbolPattern.test(suffix)) || - targetProcessedNames.length > 4 || - targetProcessedNames.some(name => !["friend", "nick", "display", "user"].includes(name.trim())) + targetProcessedNames.length > 5 || + targetProcessedNames.some(name => !["custom", "friend", "nick", "display", "user"].includes(name.trim())) ); }); @@ -218,7 +225,13 @@ function validTemplate(value: string) { } } -function getProcessedNames(author: any, truncateAllNamesWithStreamerMode: boolean, discriminators: boolean): [string | null, string | null, string | null, string | null] { +function getProcessedNames(author: any, truncateAllNamesWithStreamerMode: boolean, discriminators: boolean): { + username: string | null; + display: string | null; + nick: string | null; + friend: string | null; + custom: string | null; +} { let discriminator: string | null = null; if (discriminators) { @@ -254,7 +267,13 @@ function getProcessedNames(author: any, truncateAllNamesWithStreamerMode: boolea ? friendName[0] + "..." : friendName as string; - return [username, display, nick, friend]; + const customName: string | null = !author?.id ? null : customNicknames[author.id] || null; + const custom: string | null = !customName ? null + : StreamerModeStore.enabled && (truncateAllNamesWithStreamerMode || customName.toLowerCase() === author.username.toLowerCase()) + ? customName[0] + "..." + : customName; + + return { username, display, nick, friend, custom }; } interface mentionProps { @@ -351,8 +370,8 @@ function renderUsername( const isReaction = isReactionsTooltip || isReactionsPopout; const isVoice = type === "voiceChannel"; - const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "memberList", "profilePopout", "reactions", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreGradients", "ignoreFonts", "animateGradients", "includedNames", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]); - const { messages, replies, mentions, memberList, profilePopout, reactions, discriminators, hideDefaultAtSign, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, ignoreFonts, animateGradients, includedNames, friendNameColor, nicknameColor, displayNameColor, usernameColor, nameSeparator, triggerNameRerender } = config; + const config = hookless ? settings.store : settings.use(["messages", "replies", "mentions", "memberList", "profilePopout", "reactions", "discriminators", "hideDefaultAtSign", "truncateAllNamesWithStreamerMode", "removeDuplicates", "ignoreGradients", "ignoreFonts", "animateGradients", "includedNames", "customNameColor", "friendNameColor", "nicknameColor", "displayNameColor", "usernameColor", "nameSeparator", "triggerNameRerender"]); + const { messages, replies, mentions, memberList, profilePopout, reactions, discriminators, hideDefaultAtSign, truncateAllNamesWithStreamerMode, removeDuplicates, ignoreGradients, ignoreFonts, animateGradients, includedNames, customNameColor, friendNameColor, nicknameColor, displayNameColor, usernameColor, nameSeparator, triggerNameRerender } = config; const canUseGradient = ((author as GuildMember)?.guildId ? (GuildStore.getGuild((author as GuildMember).guildId) ?? {}).premiumFeatures?.features.includes("ENHANCED_ROLE_COLORS") : false); const textMutedValue = getComputedStyle(document.documentElement)?.getPropertyValue("--text-muted")?.trim() || "#72767d"; @@ -361,14 +380,16 @@ function renderUsername( const resolvedDisplayNameColor = author ? resolveColor(author, displayNameColor.trim(), "", canUseGradient) : null; const resolvedNicknameColor = author ? resolveColor(author, nicknameColor.trim(), "", canUseGradient) : null; const resolvedFriendNameColor = author ? resolveColor(author, friendNameColor.trim(), "", canUseGradient) : null; + const resolvedCustomNameColor = author ? resolveColor(author, customNameColor.trim(), "", canUseGradient) : null; const affixColor = { color: textMutedValue, "-webkit-text-fill-color": textMutedValue, isolation: "isolate", "white-space": "pre", "font-family": "var(--font-primary)", "letter-spacing": "normal" }; - const [username, display, nick, friend] = getProcessedNames(author, truncateAllNamesWithStreamerMode, discriminators); + const { username, display, nick, friend, custom } = getProcessedNames(author, truncateAllNamesWithStreamerMode, discriminators); const names: Record = { user: [username, resolvedUsernameColor], display: [display, resolvedDisplayNameColor], nick: [nick, resolvedNicknameColor], friend: [friend, resolvedFriendNameColor], + custom: [custom, resolvedCustomNameColor] }; const outputs: any[] = []; @@ -421,6 +442,7 @@ function renderUsername( let second = outputs.shift(); let third = outputs.shift(); let fourth = outputs.shift(); + let fifth = outputs.shift(); const firstValueWrapped = hookless ? (first.name || "") @@ -438,10 +460,15 @@ function renderUsername( ((fourth ?? {}).name || "") : wrapEmojis((fourth ?? {}).name || ""); + const fifthValueWrapped = hookless ? + ((fifth ?? {}).name || "") + : wrapEmojis((fifth ?? {}).name || ""); + first.wrapped = firstValueWrapped; second && (second.wrapped = secondValueWrapped); third && (third.wrapped = thirdValueWrapped); fourth && (fourth.wrapped = fourthValueWrapped); + fifth && (fifth.wrapped = fifthValueWrapped); if (isMessage && !messages) { return [null, null, null]; @@ -462,10 +489,14 @@ function renderUsername( return [fallbackText, <>{fallbackText}, fallbackText]; } - const prioritizeUsername = (new Set([first, second, third, fourth].filter(Boolean).map(pos => pos.name.toLowerCase())).size > 1); + const prioritizeUsername = (new Set([first, second, third, fourth, fifth].filter(Boolean).map(pos => pos.name.toLowerCase())).size > 1); if (removeDuplicates) { // Remove duplicates from back to front. Prioritize the earlier name, unless it's the username and there's more than one unique option, then prioritize it. + fifth && fourth && fifth.name.toLowerCase() === fourth.name.toLowerCase() ? fifth.type === "user" && prioritizeUsername ? fourth = null : fifth = null : null; + fifth && third && fifth.name.toLowerCase() === third.name.toLowerCase() ? fifth.type === "user" && prioritizeUsername ? third = null : fifth = null : null; + fifth && second && fifth.name.toLowerCase() === second.name.toLowerCase() ? fifth.type === "user" && prioritizeUsername ? second = null : fifth = null : null; + fifth && first && fifth.name.toLowerCase() === first.name.toLowerCase() ? fifth.type === "user" && prioritizeUsername ? first = null : fifth = null : null; fourth && third && fourth.name.toLowerCase() === third.name.toLowerCase() ? fourth.type === "user" && prioritizeUsername ? third = null : fourth = null : null; fourth && second && fourth.name.toLowerCase() === second.name.toLowerCase() ? fourth.type === "user" && prioritizeUsername ? second = null : fourth = null : null; fourth && first && fourth.name.toLowerCase() === first.name.toLowerCase() ? fourth.type === "user" && prioritizeUsername ? first = null : fourth = null : null; @@ -474,11 +505,12 @@ function renderUsername( second && first && second.name.toLowerCase() === first.name.toLowerCase() ? second.type === "user" && prioritizeUsername ? first = null : second = null : null; } - const remainingNames = [first, second, third, fourth].filter(Boolean); + const remainingNames = [first, second, third, fourth, fifth].filter(Boolean); first = remainingNames.shift(); second = remainingNames.shift(); third = remainingNames.shift(); fourth = remainingNames.shift(); + fifth = remainingNames.shift(); const useTopRoleStyle = isMention || isReactionsPopout; const topRoleStyle = resolveColor(author, "Role", "", canUseGradient); @@ -500,7 +532,8 @@ function renderUsername( const secondDataText = second && shouldAnimateSecondaryNames ? (second.prefix + second.name + second.suffix) : ""; const thirdDataText = third && shouldAnimateSecondaryNames ? (third.prefix + third.name + third.suffix) : ""; const fourthDataText = fourth && shouldAnimateSecondaryNames ? (fourth.prefix + fourth.name + fourth.suffix) : ""; - const allDataText = [firstDataText, secondDataText, thirdDataText, fourthDataText].filter(Boolean).join(nameSeparator).trim(); + const fifthDataText = fifth && shouldAnimateSecondaryNames ? (fifth.prefix + fifth.name + fifth.suffix) : ""; + const allDataText = [firstDataText, secondDataText, thirdDataText, fourthDataText, fifthDataText].filter(Boolean).join(nameSeparator).trim(); // Only mentions and reactions popouts should patch in the gradient glow or else a double glow will appear on messages. const hoveringClass = (isHovering ? " show-me-your-name-gradient-hovered" : ""); @@ -512,10 +545,12 @@ function renderUsername( const secondGroupClasses = "show-me-your-name-name-group show-me-your-name-second-name-group"; const thirdGroupClasses = "show-me-your-name-name-group show-me-your-name-third-name-group"; const fourthGroupClasses = "show-me-your-name-name-group show-me-your-name-fourth-name-group"; + const fifthGroupClasses = "show-me-your-name-name-group show-me-your-name-fifth-name-group"; const firstNameClasses = "show-me-your-name-name show-me-your-name-first-name"; const secondNameClasses = "show-me-your-name-name show-me-your-name-second-name"; const thirdNameClasses = "show-me-your-name-name show-me-your-name-third-name"; const fourthNameClasses = "show-me-your-name-name show-me-your-name-fourth-name"; + const fifthNameClasses = "show-me-your-name-name show-me-your-name-fifth-name"; const prefixClasses = "show-me-your-name-affix show-me-your-name-prefix"; const suffixClasses = "show-me-your-name-affix show-me-your-name-suffix"; @@ -549,85 +584,39 @@ function renderUsername( {first.wrapped}
)} - {second && ( + {[ + { name: second, dataText: secondDataText, groupClass: secondGroupClasses, nameClass: secondNameClasses, position: "second" }, + { name: third, dataText: thirdDataText, groupClass: thirdGroupClasses, nameClass: thirdNameClasses, position: "third" }, + { name: fourth, dataText: fourthDataText, groupClass: fourthGroupClasses, nameClass: fourthNameClasses, position: "fourth" }, + { name: fifth, dataText: fifthDataText, groupClass: fifthGroupClasses, nameClass: fifthNameClasses, position: "fifth" } + ].map(({ name, dataText, groupClass, nameClass, position }) => name && ( {nameSeparator} - {second.prefix} + {name.prefix} - {second.wrapped} + {name.wrapped} - {second.suffix} + {name.suffix} - )} - {third && ( - - - {nameSeparator} - {third.prefix} - - {third.wrapped} - - {third.suffix} - - )} - {fourth && ( - - - {nameSeparator} - {fourth.prefix} - - {fourth.wrapped} - - {fourth.suffix} - - )} + ))} ); @@ -688,6 +677,98 @@ function removeHoveringReactionPopout(id: string) { settings.store.triggerNameRerender = !settings.store.triggerNameRerender; } +function CustomNicknameModal({ modalProps, user }: { modalProps: ModalProps; user: User; }) { + const [value, setValue] = useState(customNicknames[user.id] ?? ""); + + return ( + + + + {customNicknames[user.id] ? "Change SMYN Nickname" : "Add SMYN Nickname"} + + + + + + {"Set a custom SMYN nickname for this user. Make use of it by specifying {custom} in the SMYN template settings."} + +
+ + SMYN Nickname + + + { + setValue(""); + delete customNicknames[user.id]; + await DataStore.set("SMYNCustomNicknames", customNicknames); + settings.store.triggerNameRerender = !settings.store.triggerNameRerender; + }} + > + Reset SMYN Nickname + +
+
+ + + + +
+ ); +} + +const userContextPatch: NavContextMenuPatchCallback = (children, { user }) => { + if (!user) return; + + const currentUser = UserStore.getCurrentUser(); + if (!currentUser || user.id === currentUser.id) return; + + const group = findGroupChildrenByChildId("user-profile", children); + + !group && children.push(); + (group || children).push( + openModal(props => ( + + + + ))} + /> + ); +}; + const settings = definePluginSettings({ messages: { type: OptionType.BOOLEAN, @@ -747,17 +828,17 @@ const settings = definePluginSettings({ ignoreFonts: { type: OptionType.BOOLEAN, default: false, - description: "For the second, third, and fourth names, use GG SANS regardless of the user's custom font.", + description: "For the non-primary names, use Discord's default fonts regardless of the user's custom nitro font.", }, ignoreGradients: { type: OptionType.BOOLEAN, default: true, - description: "For the second, third, and fourth names, if the role has a gradient, ignore it in favor of the value below.", + description: "For the non-primary names, if the role has a gradient, ignore it in favor of the color set below.", }, animateGradients: { type: OptionType.BOOLEAN, default: false, - description: "For the second, third, and fourth names, if the role has a gradient, animate it. This is disabled by \"Ignore Gradients\" and reduced motion.", + description: "For the non-primary names, if the role has a gradient, animate it. This is disabled by \"Ignore Gradients\" and reduced motion.", }, nameSeparator: { type: OptionType.STRING, @@ -766,10 +847,16 @@ const settings = definePluginSettings({ }, includedNames: { type: OptionType.STRING, - description: "The order to display usernames, display names, nicknames, and friend names. Use the following placeholders: {user}, {display}, {nick}, {friend}. You can provide multiple name options to use as fallbacks if one is unavailable by separating them with commas as such: {friend, nick, display}. You can have up to three prefixes and three suffixes per name.", - default: "{friend, nick} [{display}] (@{user})", + description: "The order to display usernames, display names, nicknames, friend names, and custom names. Use the following placeholders: {user}, {display}, {nick}, {friend}, {custom}. You can provide multiple name options to use as fallbacks if one is unavailable by separating them with commas as such: {custom, friend, nick}. You can have up to three prefixes and three suffixes per name.", + default: "{custom, friend, nick} [{display}] (@{user})", isValid: validTemplate, }, + customNameColor: { + type: OptionType.STRING, + description: "The color to use for the custom name you assigned a user if it's not the first displayed. Leave blank for default. Accepts hex(a), rgb(a), or hsl(a) input. Use \"Role\" to follow the user's top role color. Use \"Role+-#\" to adjust the brightness by that percentage (ex: \"Role+15\")", + default: "Role-25", + isValid: validColor, + }, friendNameColor: { type: OptionType.STRING, description: "The color to use for a friend's nickname if it's not the first displayed. Leave blank for default. Accepts hex(a), rgb(a), or hsl(a) input. Use \"Role\" to follow the user's top role color. Use \"Role+-#\" to adjust the brightness by that percentage (ex: \"Role+15\")", @@ -804,8 +891,9 @@ const settings = definePluginSettings({ export default definePlugin({ name: "ShowMeYourName", - description: "Display any permutation of nicknames, display names, and usernames in chat.", - authors: [Devs.Rini, Devs.TheKodeToad, EquicordDevs.Etorix, Devs.sadan], + description: "Display any permutation of custom nicknames, friend nicknames, server nicknames, display names, and usernames in chat.", + authors: [Devs.Rini, Devs.TheKodeToad, EquicordDevs.Etorix, Devs.sadan, EquicordDevs.Prism], + tags: ["SMYN", "Nicknames", "Custom Nicknames",], isModified: true, settings, @@ -943,6 +1031,15 @@ export default definePlugin({ } ], + async start() { + const data = await DataStore.get("SMYNCustomNicknames"); + customNicknames = data ?? {}; + }, + + contextMenus: { + "user-context": userContextPatch + }, + flux: { RELATIONSHIP_UPDATE(data) { // Allows rerendering when changing friend names. diff --git a/src/plugins/showMeYourName/style.css b/src/plugins/showMeYourName/style.css index 58bd29db7..45cfb18a0 100644 --- a/src/plugins/showMeYourName/style.css +++ b/src/plugins/showMeYourName/style.css @@ -37,4 +37,14 @@ .show-me-your-name-gradient-hovered::after { opacity: .7; transition: opacity .1s ease-in-out +} + +.show-me-your-name-modal-footer-container button { + width: 100%; +} + +.show-me-your-name-reset-button { + margin-top: 8px; + font-size: 14px; + font-weight: 500; } \ No newline at end of file diff --git a/src/plugins/silentTyping/index.tsx b/src/plugins/silentTyping/index.tsx index c4b973ed1..8637889ac 100644 --- a/src/plugins/silentTyping/index.tsx +++ b/src/plugins/silentTyping/index.tsx @@ -201,6 +201,14 @@ const SilentTypingChatToggle: ChatBarButtonFactory = ({ channel, type }) => { ); }; +function SilentTypingChatIcon() { + return ( + + + + ); +} + function getEffectiveList(): string[] { if (settings.store.defaultHidden) { if (!settings.store.disabledLocations) { @@ -288,7 +296,6 @@ export default definePlugin({ name: "SilentTyping", authors: [Devs.Ven, Devs.Rini, Devs.ImBanana, EquicordDevs.Etorix], description: "Hide your typing indicator from chat.", - dependencies: ["ChatInputButtonAPI"], isModified: true, settings, @@ -298,6 +305,10 @@ export default definePlugin({ contextMenus: { "textarea-context": ChatBarContextCheckbox }, + chatBarButton: { + icon: SilentTypingChatIcon, + render: SilentTypingChatToggle + }, patches: [ { @@ -425,6 +436,4 @@ export default definePlugin({ if (checkEnabled(channelId)) return; FluxDispatcher.dispatch({ type: "TYPING_START_LOCAL", channelId }); }, - - renderChatBarButton: SilentTypingChatToggle, }); diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index 5f1a05e54..70c22cf59 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -18,9 +18,6 @@ import "./style.css"; -import { addMemberListDecorator, removeMemberListDecorator } from "@api/MemberListDecorators"; -import { addMessageDecoration, removeMessageDecoration } from "@api/MessageDecorations"; -import { addNicknameIcon, removeNicknameIcon } from "@api/NicknameIcons"; import { definePluginSettings } from "@api/Settings"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -52,9 +49,22 @@ export default definePlugin({ name: "UserVoiceShow", description: "Shows an indicator when a user is in a Voice Channel", authors: [Devs.Nuckyz, Devs.LordElias, EquicordDevs.omaw], - dependencies: ["NicknameIconsAPI", "MemberListDecoratorsAPI", "MessageDecorationsAPI"], settings, + renderNicknameIcon({ userId }) { + if (!settings.store.showInUserProfileModal) return null; + return ( + + ); + }, + renderMemberListDecorator({ user }) { + if (!settings.store.showInMemberList) return null; + return user == null ? null : ; + }, + renderMessageDecoration({ message }) { + if (!settings.store.showInMessages) return null; + return message?.author == null ? null : ; + }, patches: [ // Friends List { @@ -67,23 +77,5 @@ export default definePlugin({ } ], - start() { - if (settings.store.showInUserProfileModal) { - addNicknameIcon("UserVoiceShow", ({ userId }) => ); - } - if (settings.store.showInMemberList) { - addMemberListDecorator("UserVoiceShow", ({ user }) => user == null ? null : ); - } - if (settings.store.showInMessages) { - addMessageDecoration("UserVoiceShow", ({ message }) => message?.author == null ? null : ); - } - }, - - stop() { - removeNicknameIcon("UserVoiceShow"); - removeMemberListDecorator("UserVoiceShow"); - removeMessageDecoration("UserVoiceShow"); - }, - VoiceChannelIndicator }); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index b5ab319d6..78010ea6c 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -632,7 +632,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ alfred: { name: "alfred", id: 1038466644353232967n - } + }, } satisfies Record); export const EquicordDevs = Object.freeze({ @@ -1205,7 +1205,7 @@ export const EquicordDevs = Object.freeze({ name: "neoarz", id: 1015372540937502851n }, - KamiRu:{ + KamiRu: { name: "KamiRu", id: 819191621676695563n }, diff --git a/src/utils/patches.ts b/src/utils/patches.ts index 0bcd2ec12..ea22bf2d4 100644 --- a/src/utils/patches.ts +++ b/src/utils/patches.ts @@ -24,9 +24,11 @@ import { Patch, PatchReplacement, ReplaceFn } from "./types"; // @ts-expect-error "RegExp.escape" is very new and not yet in DOM types const escapeRegex = RegExp.escape || ((s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")); -export function getReplacement(isString: boolean, hashed: string) { +export function getReplacement(isString: boolean, hashed: string, hash: boolean = false) { const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/"); + if (hash) return hashed; + if (hasSpecialChars) { return isString ? `["${hashed}"]` @@ -48,7 +50,7 @@ function canonicalizeMatchCompatString(str: string) { const re = /#{intl::([\w$+/]*)(?:::(\w+))?}/g; for (const match of str.matchAll(re)) { result += escapeRegex(str.slice(lastIndex, match.index)); - result += getCompatReplacement(match[1]); + result += match[2] === "raw" ? getReplacement(false, match[1]) : match[2] === "hash" ? getReplacement(false, runtimeHashMessageKey(match[1]), true) : getCompatReplacement(match[1]); lastIndex = (match.index ?? 0) + match[0].length; } result += escapeRegex(str.slice(lastIndex)); @@ -63,6 +65,7 @@ export function canonicalizeMatch(match: T): T extend let partialCanon = typeof match === "string" ? match : match.source; partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => { if (modifier === "raw") return getReplacement(false, key); + if (modifier === "hash") return getReplacement(false, runtimeHashMessageKey(key), true); return getCompatReplacement(key); }); diff --git a/src/utils/types.ts b/src/utils/types.ts index d882dd785..f70902291 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -16,9 +16,11 @@ * along with this program. If not, see . */ +import { AudioProcessor } from "@api/AudioPlayer"; import type { ProfileBadge } from "@api/Badges"; import type { ChatBarButtonData, ChatBarButtonFactory } from "@api/ChatButtons"; import type { NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { HeaderBarButtonFactory } from "@api/HeaderBar"; import type { MemberListDecoratorFactory } from "@api/MemberListDecorators"; import type { MessageAccessoryFactory } from "@api/MessageAccessories"; import type { MessageDecorationFactory } from "@api/MessageDecorations"; @@ -195,7 +197,11 @@ export interface PluginDef { renderMessageDecoration?: MessageDecorationFactory; renderMemberListDecorator?: MemberListDecoratorFactory; + + // Custom renderNicknameIcon?: NicknameIconFactory; + renderHeaderBarButton?: HeaderBarButtonFactory; + audioProcessor?: AudioProcessor; // TODO: Remove eventually /** diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 9ad235e8f..5d527ca6f 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -18,6 +18,7 @@ import type * as t from "@vencord/discord-types"; import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "@webpack"; +import type * as TSPattern from "ts-pattern"; export let FluxDispatcher: t.FluxDispatcher; waitFor(["dispatch", "subscribe"], m => { @@ -51,7 +52,7 @@ export const useDrag = findByCodeLazy("useDrag::spec.begin was deprecated"); // you cant make a better finder i love that they remove display names sm export const useDrop = findByCodeLazy(".options);return", ".collect,"); -export const { match, P }: Pick = mapMangledModuleLazy("@ts-pattern/matcher", { +export const { match, P }: Pick = mapMangledModuleLazy("@ts-pattern/matcher", { match: filters.byCode("return new"), P: filters.byProps("when") });