Merge branch 'dev' into main

This commit is contained in:
byeoon 2024-05-02 10:39:41 -04:00 committed by GitHub
commit 530e19e266
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 109 additions and 46 deletions

View file

@ -36,7 +36,7 @@ export interface ProfileBadge {
image?: string; image?: string;
link?: string; link?: string;
/** Action to perform when you click the badge */ /** Action to perform when you click the badge */
onClick?(): void; onClick?(event: React.MouseEvent<HTMLButtonElement, MouseEvent>, props: BadgeUserArgs): void;
/** Should the user display this badge? */ /** Should the user display this badge? */
shouldShow?(userInfo: BadgeUserArgs): boolean; shouldShow?(userInfo: BadgeUserArgs): boolean;
/** Optional props (e.g. style) for the badge, ignored for component badges */ /** Optional props (e.g. style) for the badge, ignored for component badges */
@ -87,9 +87,7 @@ export function _getBadges(args: BadgeUserArgs) {
export interface BadgeUserArgs { export interface BadgeUserArgs {
user: User; user: User;
profile: Profile; guildId: string;
premiumSince: Date;
premiumGuildSince?: Date;
} }
interface ConnectedAccount { interface ConnectedAccount {

View file

@ -9,10 +9,12 @@ import "./contributorModal.css";
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Link } from "@components/Link";
import { DevsById } from "@utils/constants"; import { DevsById } from "@utils/constants";
import { fetchUserProfile, getTheme, Theme } from "@utils/discord"; import { fetchUserProfile, getTheme, Theme } from "@utils/discord";
import { pluralise } from "@utils/misc";
import { ModalContent, ModalRoot, openModal } from "@utils/modal"; 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 { User } from "discord-types/general";
import Plugins from "~plugins"; import Plugins from "~plugins";
@ -72,6 +74,8 @@ function ContributorModal({ user }: { user: User; }) {
.sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false)); .sort((a, b) => Number(a.required ?? false) - Number(b.required ?? false));
}, [user.id, user.username]); }, [user.id, user.username]);
const ContributedHyperLink = <Link href="https://vencord.dev/source">contributed</Link>;
return ( return (
<> <>
<div className={cl("header")}> <div className={cl("header")}>
@ -84,30 +88,48 @@ function ContributorModal({ user }: { user: User; }) {
<div className={cl("links")}> <div className={cl("links")}>
{website && ( {website && (
<MaskedLink <Tooltip text={website}>
href={"https://" + website} {props => (
> <MaskedLink {...props} href={"https://" + website}>
<WebsiteIcon /> <WebsiteIcon />
</MaskedLink> </MaskedLink>
)}
</Tooltip>
)} )}
{githubName && ( {githubName && (
<MaskedLink href={`https://github.com/${githubName}`}> <Tooltip text={githubName}>
<GithubIcon /> {props => (
</MaskedLink> <MaskedLink {...props} href={`https://github.com/${githubName}`}>
<GithubIcon />
</MaskedLink>
)}
</Tooltip>
)} )}
</div> </div>
</div> </div>
<div className={cl("plugins")}> {plugins.length ? (
{plugins.map(p => <Forms.FormText>
<PluginCard This person has {ContributedHyperLink} to {pluralise(plugins.length, "plugin")}!
key={p.name} </Forms.FormText>
plugin={p} ) : (
disabled={p.required ?? false} <Forms.FormText>
onRestartNeeded={() => showToast("Restart to apply changes!")} This person has not made any plugins. They likely {ContributedHyperLink} to Vencord in other ways!
/> </Forms.FormText>
)} )}
</div>
{!!plugins.length && (
<div className={cl("plugins")}>
{plugins.map(p =>
<PluginCard
key={p.name}
plugin={p}
disabled={p.required ?? false}
onRestartNeeded={() => showToast("Restart to apply changes!")}
/>
)}
</div>
)}
</> </>
); );
} }

View file

@ -25,11 +25,13 @@
display: block; display: block;
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 16px; width: 32px;
background: var(--background-tertiary); background: var(--background-tertiary);
z-index: -1; z-index: -1;
left: -16px; left: -32px;
top: 0; top: 0;
border-top-left-radius: 9999px;
border-bottom-left-radius: 9999px;
} }
.vc-author-modal-avatar { .vc-author-modal-avatar {
@ -55,4 +57,5 @@
.vc-author-modal-plugins { .vc-author-modal-plugins {
display: grid; display: grid;
gap: 0.5em; gap: 0.5em;
margin-top: 0.75em;
} }

View file

@ -34,14 +34,13 @@ const ContributorBadge: ProfileBadge = {
description: "Vencord Contributor", description: "Vencord Contributor",
image: CONTRIBUTOR_BADGE, image: CONTRIBUTOR_BADGE,
position: BadgePosition.START, 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), 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));
}
}; };
let DonorBadges = {} as Record<string, Array<Record<"tooltip" | "badge", string>>>; let DonorBadges = {} as Record<string, Array<Record<"tooltip" | "badge", string>>>;
@ -85,7 +84,7 @@ export default definePlugin({
// conditionally override their onClick with badge.onClick if it exists // conditionally override their onClick with badge.onClick if it exists
{ {
match: /href:(\i)\.link/, match: /href:(\i)\.link/,
replace: "...($1.onClick && { onClick: $1.onClick }),$&" replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&"
} }
] ]
} }

View file

@ -16,17 +16,31 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; 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({ export default definePlugin({
name: "NoTrack", 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], authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz, Devs.Arrow],
required: true, required: true,
settings,
patches: [ patches: [
{ {
find: "AnalyticsActionHandlers.handle", find: "AnalyticsActionHandlers.handle",
predicate: () => settings.store.disableAnalytics,
replacement: { replacement: {
match: /^.+$/, match: /^.+$/,
replace: "()=>{}", replace: "()=>{}",
@ -44,11 +58,11 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /this\._intervalId=/, match: /this\._intervalId=/,
replace: "this._intervalId=undefined&&" replace: "this._intervalId=void 0&&"
}, },
{ {
match: /(increment\(\i\){)/, match: /(?:increment|distribution)\(\i(?:,\i)?\){/g,
replace: "$1return;" replace: "$&return;"
} }
] ]
}, },

View file

@ -9,9 +9,8 @@ import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; 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 container = findByPropsLazy("memberSinceWrapper");
const { getCreatedAtDate } = findByPropsLazy("getCreatedAtDate"); const { getCreatedAtDate } = findByPropsLazy("getCreatedAtDate");
const clydeMoreInfo = findByPropsLazy("clydeMoreInfo"); const clydeMoreInfo = findByPropsLazy("clydeMoreInfo");

View file

@ -114,3 +114,7 @@ export function identity<T>(value: T): T {
export const isMobile = navigator.userAgent.includes("Mobi"); export const isMobile = navigator.userAgent.includes("Mobi");
export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id); export const isPluginDev = (id: string) => Object.hasOwn(DevsById, id);
export function pluralise(amount: number, singular: string, plural = singular + "s") {
return amount === 1 ? `${amount} ${singular}` : `${amount} ${plural}`;
}

View file

@ -36,6 +36,7 @@ export let Tooltip: t.Tooltip;
export let TextInput: t.TextInput; export let TextInput: t.TextInput;
export let TextArea: t.TextArea; export let TextArea: t.TextArea;
export let Text: t.Text; export let Text: t.Text;
export let Heading: t.HeadingTag;
export let Select: t.Select; export let Select: t.Select;
export let SearchableSelect: t.SearchableSelect; export let SearchableSelect: t.SearchableSelect;
export let Slider: t.Slider; export let Slider: t.Slider;
@ -59,6 +60,28 @@ export const Flex = waitForComponent<t.Flex>("Flex", ["Justify", "Align", "Wrap"
export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal");
waitFor(["FormItem", "Button"], m => { 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; Forms = m;
}); });

View file

@ -20,23 +20,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 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 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 Margins = Record<"marginTop16" | "marginTop8" | "marginBottom8" | "marginTop20" | "marginBottom20", string>;
export type ButtonLooks = Record<"FILLED" | "INVERTED" | "OUTLINED" | "LINK" | "BLANK", string>; export type ButtonLooks = Record<"FILLED" | "INVERTED" | "OUTLINED" | "LINK" | "BLANK", string>;
export type TextProps = PropsWithChildren<HtmlHTMLAttributes<HTMLDivElement> & { export type TextProps = PropsWithChildren<HtmlHTMLAttributes<HTMLDivElement> & {
variant?: TextVariant; variant?: TextVariant;
tag?: "div" | "span" | "p" | "strong" | Heading; tag?: "div" | "span" | "p" | "strong" | HeadingTag;
selectable?: boolean; selectable?: boolean;
lineClamp?: number; lineClamp?: number;
}>; }>;
export type Text = ComponentType<TextProps>; export type Text = ComponentType<TextProps>;
export type Heading = ComponentType<TextProps>;
export type FormTitle = ComponentType<HTMLProps<HTMLTitleElement> & PropsWithChildren<{ export type FormTitle = ComponentType<HTMLProps<HTMLTitleElement> & PropsWithChildren<{
/** default is h5 */ /** default is h5 */
tag?: Heading; tag?: HeadingTag;
faded?: boolean; faded?: boolean;
disabled?: boolean; disabled?: boolean;
required?: boolean; required?: boolean;
@ -45,7 +46,7 @@ export type FormTitle = ComponentType<HTMLProps<HTMLTitleElement> & PropsWithChi
export type FormSection = ComponentType<PropsWithChildren<{ export type FormSection = ComponentType<PropsWithChildren<{
/** default is h5 */ /** default is h5 */
tag?: Heading; tag?: HeadingTag;
className?: string; className?: string;
titleClassName?: string; titleClassName?: string;
titleId?: string; titleId?: string;
@ -455,5 +456,5 @@ export type Avatar = ComponentType<PropsWithChildren<{
}>>; }>>;
type FocusLock = ComponentType<PropsWithChildren<{ type FocusLock = ComponentType<PropsWithChildren<{
containerRef: RefObject<HTMLElement> containerRef: RefObject<HTMLElement>;
}>>; }>>;