diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 2b4f4563b..129482142 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -36,7 +36,7 @@ export interface ProfileBadge { image?: string; link?: string; /** Action to perform when you click the badge */ - onClick?(): void; + onClick?(event: React.MouseEvent, props: BadgeUserArgs): void; /** Should the user display this badge? */ shouldShow?(userInfo: BadgeUserArgs): boolean; /** Optional props (e.g. style) for the badge, ignored for component badges */ @@ -89,9 +89,7 @@ export function _getBadges(args: BadgeUserArgs) { export interface BadgeUserArgs { user: User; - profile: Profile; - premiumSince: Date; - premiumGuildSince?: Date; + guildId: string; } interface ConnectedAccount { diff --git a/src/components/PluginSettings/ContributorModal.tsx b/src/components/PluginSettings/ContributorModal.tsx index 609375eb9..9daa8c2a6 100644 --- a/src/components/PluginSettings/ContributorModal.tsx +++ b/src/components/PluginSettings/ContributorModal.tsx @@ -10,9 +10,11 @@ import { useSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { DevsById, EquicordDevsById } from "@utils/constants"; +import { Link } from "@components/Link"; import { fetchUserProfile, getTheme, Theme } from "@utils/discord"; +import { pluralise } from "@utils/misc"; import { ModalContent, ModalRoot, openModal } from "@utils/modal"; -import { Forms, MaskedLink, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common"; +import { Forms, MaskedLink, showToast, Tooltip, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common"; import { User } from "discord-types/general"; import Plugins from "~plugins"; @@ -72,6 +74,8 @@ function ContributorModal({ user }: { user: User; }) { .sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false)); }, [user.id, user.username]); + const ContributedHyperLink = contributed; + return ( <>
@@ -84,30 +88,48 @@ function ContributorModal({ user }: { user: User; }) {
{website && ( - - - + + {props => ( + + + + )} + )} {githubName && ( - - - + + {props => ( + + + + )} + )}
-
- {plugins.map(p => - showToast("Restart to apply changes!")} - /> - )} -
+ {plugins.length ? ( + + This person has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}! + + ) : ( + + This person has not made any plugins. They likely {ContributedHyperLink} to Vencord in other ways! + + )} + + {!!plugins.length && ( +
+ {plugins.map(p => + showToast("Restart to apply changes!")} + /> + )} +
+ )} ); } diff --git a/src/components/PluginSettings/contributorModal.css b/src/components/PluginSettings/contributorModal.css index 09f0103fd..ad2f1330c 100644 --- a/src/components/PluginSettings/contributorModal.css +++ b/src/components/PluginSettings/contributorModal.css @@ -25,11 +25,13 @@ display: block; position: absolute; height: 100%; - width: 16px; + width: 32px; background: var(--background-tertiary); z-index: -1; - left: -16px; + left: -32px; top: 0; + border-top-left-radius: 9999px; + border-bottom-left-radius: 9999px; } .vc-author-modal-avatar { @@ -55,4 +57,5 @@ .vc-author-modal-plugins { display: grid; gap: 0.5em; + margin-top: 0.75em; } diff --git a/src/plugins/_api/badges.tsx b/src/plugins/_api/badges.tsx index 5f2cf4833..054649464 100644 --- a/src/plugins/_api/badges.tsx +++ b/src/plugins/_api/badges.tsx @@ -35,14 +35,13 @@ const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", image: CONTRIBUTOR_BADGE, position: BadgePosition.START, - props: { - style: { - borderRadius: "50%", - transform: "scale(0.9)" // The image is a bit too big compared to default badges - } - }, shouldShow: ({ user }) => isPluginDev(user.id), - link: "https://github.com/Vendicated/Vencord" + onClick(_, { user }) { + // circular import shenanigans + const { openContributorModal } = require("@components/PluginSettings/ContributorModal") as typeof import("@components/PluginSettings/ContributorModal"); + // setImmediate is needed to run on later tick to workaround limitation in proxyLazy + setImmediate(() => openContributorModal(user)); + } }; const EquicordContributorBadge: ProfileBadge = { @@ -106,7 +105,7 @@ export default definePlugin({ // conditionally override their onClick with badge.onClick if it exists { match: /href:(\i)\.link/, - replace: "...($1.onClick && { onClick: $1.onClick }),$&" + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&" } ] } diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 424e62c04..67f6c6448 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -16,17 +16,31 @@ * along with this program. If not, see . */ +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; + +const settings = definePluginSettings({ + disableAnalytics: { + type: OptionType.BOOLEAN, + description: "Disable Discord's tracking (analytics/'science')", + default: true, + restartNeeded: true + } +}); export default definePlugin({ name: "NoTrack", - description: "Disable Discord's tracking ('science'), metrics and Sentry crash reporting", + description: "Disable Discord's tracking (analytics/'science'), metrics and Sentry crash reporting", authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz, Devs.Arrow], required: true, + + settings, + patches: [ { find: "AnalyticsActionHandlers.handle", + predicate: () => settings.store.disableAnalytics, replacement: { match: /^.+$/, replace: "()=>{}", @@ -44,11 +58,11 @@ export default definePlugin({ replacement: [ { match: /this\._intervalId=/, - replace: "this._intervalId=undefined&&" + replace: "this._intervalId=void 0&&" }, { - match: /(increment\(\i\){)/, - replace: "$1return;" + match: /(?:increment|distribution)\(\i(?:,\i)?\){/g, + replace: "$&return;" } ] }, diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index c356271a9..b3bd91b47 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -9,9 +9,8 @@ import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { React, RelationshipStore } from "@webpack/common"; +import { Heading, React, RelationshipStore, Text } from "@webpack/common"; -const { Heading, Text } = findByPropsLazy("Heading", "Text"); const container = findByPropsLazy("memberSinceWrapper"); const { getCreatedAtDate } = findByPropsLazy("getCreatedAtDate"); const clydeMoreInfo = findByPropsLazy("clydeMoreInfo"); diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx index 980629126..2042ed9c7 100644 --- a/src/plugins/resurrectHome/index.tsx +++ b/src/plugins/resurrectHome/index.tsx @@ -91,7 +91,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "ResurrectHome", - description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking the Server Guide.", + description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking a server.", authors: [Devs.Dolfies, Devs.Nuckyz], settings, @@ -151,7 +151,7 @@ export default definePlugin({ find: "487e85_1", replacement: { match: /(?<=text:(\i)\?\i\.\i\.Messages\.SERVER_GUIDE:\i\.\i\.Messages\.GUILD_HOME,)/, - replace: "badge:$self.ViewServerHomeButton({serverGuide:$1})," + replace: "trailing:$self.ViewServerHomeButton({serverGuide:$1})," } }, // Disable view Server Home override when the Server Home is unmouted diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index dafdd1a27..56b224da8 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -94,7 +94,7 @@ export default definePlugin({ find: "renderPrioritySpeaker", replacement: [ { - match: /renderName\(\).{0,100}speaking:.+?\.clanTag.+?"div",{/, + match: /renderName\(\){.+?usernameSpeaking\]:.+?(?=children)/, replace: "$&...$self.getVoiceProps(this.props)," } ], diff --git a/src/utils/misc.tsx b/src/utils/misc.tsx index 19a06875b..1c7f63b59 100644 --- a/src/utils/misc.tsx +++ b/src/utils/misc.tsx @@ -115,3 +115,7 @@ export const isMobile = navigator.userAgent.includes("Mobi"); export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id); export const isEquicordPluginDev = (id: string) => Object.hasOwn(EquicordDevsById, id); + +export function pluralise(amount: number, singular: string, plural = singular + "s") { + return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`; +} diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 0c25e74cd..5bb0c4f67 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -35,6 +35,7 @@ export let Tooltip: t.Tooltip; export let TextInput: t.TextInput; export let TextArea: t.TextArea; export let Text: t.Text; +export let Heading: t.HeadingTag; export let Select: t.Select; export let SearchableSelect: t.SearchableSelect; export let Slider: t.Slider; @@ -58,6 +59,28 @@ export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap" export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); waitFor(["FormItem", "Button"], m => { - ({ useToken, Card, Button, FormSwitch: Switch, Tooltip, TextInput, TextArea, Text, Select, SearchableSelect, Slider, ButtonLooks, TabBar, Popout, Dialog, Paginator, ScrollerThin, Clickable, Avatar, FocusLock } = m); + ({ + useToken, + Card, + Button, + FormSwitch: Switch, + Tooltip, + TextInput, + TextArea, + Text, + Select, + SearchableSelect, + Slider, + ButtonLooks, + TabBar, + Popout, + Dialog, + Paginator, + ScrollerThin, + Clickable, + Avatar, + FocusLock, + Heading + } = m); Forms = m; }); diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index b2fa2ed2e..4a7ad2cd9 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -21,23 +21,24 @@ import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttribute export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>; -export type Heading = `h${1 | 2 | 3 | 4 | 5 | 6}`; +export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`; export type Margins = Record<"marginTop16" | "marginTop8" | "marginBottom8" | "marginTop20" | "marginBottom20", string>; export type ButtonLooks = Record<"FILLED" | "INVERTED" | "OUTLINED" | "LINK" | "BLANK", string>; export type TextProps = PropsWithChildren & { variant?: TextVariant; - tag?: "div" | "span" | "p" | "strong" | Heading; + tag?: "div" | "span" | "p" | "strong" | HeadingTag; selectable?: boolean; lineClamp?: number; }>; export type Text = ComponentType; +export type Heading = ComponentType; export type FormTitle = ComponentType & PropsWithChildren<{ /** default is h5 */ - tag?: Heading; + tag?: HeadingTag; faded?: boolean; disabled?: boolean; required?: boolean; @@ -46,7 +47,7 @@ export type FormTitle = ComponentType & PropsWithChi export type FormSection = ComponentType