Merge branch 'immediate-finds' into immediate-finds-modules-proxy
This commit is contained in:
commit
a04ccd0d11
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"minimum_chrome_version": "91",
|
"minimum_chrome_version": "111",
|
||||||
|
|
||||||
"name": "Vencord Web",
|
"name": "Vencord Web",
|
||||||
"description": "The cutest Discord mod now in your browser",
|
"description": "The cutest Discord mod now in your browser",
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "vencord-firefox@vendicated.dev",
|
"id": "vencord-firefox@vendicated.dev",
|
||||||
"strict_min_version": "91.0"
|
"strict_min_version": "128.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
import * as DataStore from "@api/DataStore";
|
import * as DataStore from "@api/DataStore";
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
|
import { Flex } from "@components/Flex";
|
||||||
|
import { openNotificationSettingsModal } from "@components/VencordSettings/NotificationSettings";
|
||||||
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
|
||||||
|
@ -170,8 +172,14 @@ function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
<Flex>
|
||||||
|
<Button onClick={openNotificationSettingsModal}>
|
||||||
|
Notification Settings
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={log.length === 0}
|
disabled={log.length === 0}
|
||||||
|
color={Button.Colors.RED}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Alerts.show({
|
Alerts.show({
|
||||||
title: "Are you sure?",
|
title: "Are you sure?",
|
||||||
|
@ -188,6 +196,7 @@ function LogModal({ modalProps, close }: { modalProps: ModalProps; close(): void
|
||||||
>
|
>
|
||||||
Clear Notification Log
|
Clear Notification Log
|
||||||
</Button>
|
</Button>
|
||||||
|
</Flex>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
);
|
);
|
||||||
|
|
28
src/components/Grid.tsx
Normal file
28
src/components/Grid.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { CSSProperties } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
columns: number;
|
||||||
|
gap?: string;
|
||||||
|
inline?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Grid(props: Props & JSX.IntrinsicElements["div"]) {
|
||||||
|
const style: CSSProperties = {
|
||||||
|
display: props.inline ? "inline-grid" : "grid",
|
||||||
|
gridTemplateColumns: `repeat(${props.columns}, 1fr)`,
|
||||||
|
gap: props.gap,
|
||||||
|
...props.style
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...props} style={style}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -18,19 +18,17 @@
|
||||||
|
|
||||||
import "./iconStyles.css";
|
import "./iconStyles.css";
|
||||||
|
|
||||||
|
import { getTheme, Theme } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { i18n } from "@webpack/common";
|
import { i18n } from "@webpack/common";
|
||||||
import type { PropsWithChildren, SVGProps } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
interface BaseIconProps extends IconProps {
|
interface BaseIconProps extends IconProps {
|
||||||
viewBox: string;
|
viewBox: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IconProps extends SVGProps<SVGSVGElement> {
|
type IconProps = JSX.IntrinsicElements["svg"];
|
||||||
className?: string;
|
type ImageProps = JSX.IntrinsicElements["img"];
|
||||||
height?: string | number;
|
|
||||||
width?: string | number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
function Icon({ height = 24, width = 24, className, children, viewBox, ...svgProps }: PropsWithChildren<BaseIconProps>) {
|
||||||
return (
|
return (
|
||||||
|
@ -329,3 +327,88 @@ export function NotesIcon(props: IconProps) {
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function FolderIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-folder-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M2 5a3 3 0 0 1 3-3h3.93a2 2 0 0 1 1.66.9L12 5h7a3 3 0 0 1 3 3v11a3 3 0 0 1-3 3H5a3 3 0 0 1-3-3V5Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LogIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-log-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M3.11 8H6v10.82c0 .86.37 1.68 1 2.27.46.43 1.02.71 1.63.84A1 1 0 0 0 9 22h10a4 4 0 0 0 4-4v-1a2 2 0 0 0-2-2h-1V5a3 3 0 0 0-3-3H4.67c-.87 0-1.7.32-2.34.9-.63.6-1 1.42-1 2.28 0 .71.3 1.35.52 1.75a5.35 5.35 0 0 0 .48.7l.01.01h.01L3.11 7l-.76.65a1 1 0 0 0 .76.35Zm1.56-4c-.38 0-.72.14-.97.37-.24.23-.37.52-.37.81a1.69 1.69 0 0 0 .3.82H6v-.83c0-.29-.13-.58-.37-.8C5.4 4.14 5.04 4 4.67 4Zm5 13a3.58 3.58 0 0 1 0 3H19a2 2 0 0 0 2-2v-1H9.66ZM3.86 6.35ZM11 8a1 1 0 1 0 0 2h5a1 1 0 1 0 0-2h-5Zm-1 5a1 1 0 0 1 1-1h5a1 1 0 1 1 0 2h-5a1 1 0 0 1-1-1Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RestartIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-restart-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 0 1 14.93-4H15a1 1 0 1 0 0 2h6a1 1 0 0 0 1-1V3a1 1 0 1 0-2 0v3a9.98 9.98 0 0 0-18 6 10 10 0 0 0 16.29 7.78 1 1 0 0 0-1.26-1.56A8 8 0 0 1 4 12Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PaintbrushIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<Icon
|
||||||
|
{...props}
|
||||||
|
className={classes(props.className, "vc-paintbrush-icon")}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M15.35 7.24C15.9 6.67 16 5.8 16 5a3 3 0 1 1 3 3c-.8 0-1.67.09-2.24.65a1.5 1.5 0 0 0 0 2.11l1.12 1.12a3 3 0 0 1 0 4.24l-5 5a3 3 0 0 1-4.25 0l-5.76-5.75a3 3 0 0 1 0-4.24l4.04-4.04.97-.97a3 3 0 0 1 4.24 0l1.12 1.12c.58.58 1.52.58 2.1 0ZM6.9 9.9 4.3 12.54a1 1 0 0 0 0 1.42l2.17 2.17.83-.84a1 1 0 0 1 1.42 1.42l-.84.83.59.59 1.83-1.84a1 1 0 0 1 1.42 1.42l-1.84 1.83.17.17a1 1 0 0 0 1.42 0l2.63-2.62L6.9 9.9Z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
||||||
|
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
||||||
|
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
||||||
|
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
||||||
|
|
||||||
|
export function GithubIcon(props: ImageProps) {
|
||||||
|
const src = getTheme() === Theme.Light
|
||||||
|
? GithubIconLight
|
||||||
|
: GithubIconDark;
|
||||||
|
|
||||||
|
return <img {...props} src={src} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function WebsiteIcon(props: ImageProps) {
|
||||||
|
const src = getTheme() === Theme.Light
|
||||||
|
? WebsiteIconLight
|
||||||
|
: WebsiteIconDark;
|
||||||
|
|
||||||
|
return <img {...props} src={src} />;
|
||||||
|
}
|
||||||
|
|
|
@ -6,22 +6,16 @@
|
||||||
|
|
||||||
import "./LinkIconButton.css";
|
import "./LinkIconButton.css";
|
||||||
|
|
||||||
import { getTheme, Theme } from "@utils/discord";
|
|
||||||
import { MaskedLink, Tooltip } from "@webpack/common";
|
import { MaskedLink, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
import { GithubIcon, WebsiteIcon } from "..";
|
||||||
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
|
||||||
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
|
||||||
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
|
||||||
|
|
||||||
export function GithubIcon() {
|
export function GithubLinkIcon() {
|
||||||
const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark;
|
return <GithubIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
||||||
return <img src={src} aria-hidden className={"vc-settings-modal-link-icon"} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WebsiteIcon() {
|
export function WebsiteLinkIcon() {
|
||||||
const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark;
|
return <WebsiteIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
||||||
return <img src={src} aria-hidden className={"vc-settings-modal-link-icon"} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -41,5 +35,5 @@ function LinkIcon({ text, href, Icon }: Props & { Icon: React.ComponentType; })
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebsiteButton = (props: Props) => <LinkIcon {...props} Icon={WebsiteIcon} />;
|
export const WebsiteButton = (props: Props) => <LinkIcon {...props} Icon={WebsiteLinkIcon} />;
|
||||||
export const GithubButton = (props: Props) => <LinkIcon {...props} Icon={GithubIcon} />;
|
export const GithubButton = (props: Props) => <LinkIcon {...props} Icon={GithubLinkIcon} />;
|
||||||
|
|
|
@ -27,7 +27,7 @@ import { gitRemote } from "@shared/vencordUserAgent";
|
||||||
import { proxyLazy } from "@utils/lazy";
|
import { proxyLazy } from "@utils/lazy";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, isObjectEmpty } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { OptionType, Plugin } from "@utils/types";
|
import { OptionType, Plugin } from "@utils/types";
|
||||||
import { findByProps, findComponentByCode } from "@webpack";
|
import { findByProps, findComponentByCode } from "@webpack";
|
||||||
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common";
|
||||||
|
@ -310,3 +310,13 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
||||||
</ModalRoot>
|
</ModalRoot>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openPluginModal(plugin: Plugin, onRestartNeeded?: (pluginName: string) => void) {
|
||||||
|
openModal(modalProps => (
|
||||||
|
<PluginModal
|
||||||
|
{...modalProps}
|
||||||
|
plugin={plugin}
|
||||||
|
onRestartNeeded={() => onRestartNeeded?.(plugin.name)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { showNotice } from "@api/Notices";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { CogWheel, InfoIcon } from "@components/Icons";
|
import { CogWheel, InfoIcon } from "@components/Icons";
|
||||||
import PluginModal from "@components/PluginSettings/PluginModal";
|
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||||
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
||||||
import { SettingsTab } from "@components/VencordSettings/shared";
|
import { SettingsTab } from "@components/VencordSettings/shared";
|
||||||
import { ChangeList } from "@utils/ChangeList";
|
import { ChangeList } from "@utils/ChangeList";
|
||||||
|
@ -31,7 +31,6 @@ import { proxyLazy } from "@utils/lazy";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, isObjectEmpty } from "@utils/misc";
|
import { classes, isObjectEmpty } from "@utils/misc";
|
||||||
import { openModalLazy } from "@utils/modal";
|
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Plugin } from "@utils/types";
|
import { Plugin } from "@utils/types";
|
||||||
import { findByProps } from "@webpack";
|
import { findByProps } from "@webpack";
|
||||||
|
@ -45,7 +44,7 @@ const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import
|
||||||
const cl = classNameFactory("vc-plugins-");
|
const cl = classNameFactory("vc-plugins-");
|
||||||
const logger = new Logger("PluginSettings", "#a6d189");
|
const logger = new Logger("PluginSettings", "#a6d189");
|
||||||
|
|
||||||
const InputStyles = findByProps("inputDefault", "inputWrapper");
|
const InputStyles = findByProps("inputWrapper", "inputDefault", "error");
|
||||||
const ButtonClasses = findByProps("button", "disabled", "enabled");
|
const ButtonClasses = findByProps("button", "disabled", "enabled");
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,14 +95,6 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
||||||
|
|
||||||
const isEnabled = () => settings.enabled ?? false;
|
const isEnabled = () => settings.enabled ?? false;
|
||||||
|
|
||||||
function openModal() {
|
|
||||||
openModalLazy(async () => {
|
|
||||||
return modalProps => {
|
|
||||||
return <PluginModal {...modalProps} plugin={plugin} onRestartNeeded={() => onRestartNeeded(plugin.name)} />;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleEnabled() {
|
function toggleEnabled() {
|
||||||
const wasEnabled = isEnabled();
|
const wasEnabled = isEnabled();
|
||||||
|
|
||||||
|
@ -160,7 +151,11 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
infoButton={
|
infoButton={
|
||||||
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
|
<button
|
||||||
|
role="switch"
|
||||||
|
onClick={() => openPluginModal(plugin, onRestartNeeded)}
|
||||||
|
className={classes(ButtonClasses.button, cl("info-button"))}
|
||||||
|
>
|
||||||
{plugin.options && !isObjectEmpty(plugin.options)
|
{plugin.options && !isObjectEmpty(plugin.options)
|
||||||
? <CogWheel />
|
? <CogWheel />
|
||||||
: <InfoIcon />}
|
: <InfoIcon />}
|
||||||
|
@ -339,8 +334,8 @@ export default function PluginSettings() {
|
||||||
Filters
|
Filters
|
||||||
</Forms.FormTitle>
|
</Forms.FormTitle>
|
||||||
|
|
||||||
<div className={cl("filter-controls")}>
|
<div className={classes(Margins.bottom20, cl("filter-controls"))}>
|
||||||
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} className={Margins.bottom20} />
|
<TextInput autoFocus value={searchValue.value} placeholder="Search for a plugin..." onChange={onSearch} />
|
||||||
<div className={InputStyles.inputWrapper}>
|
<div className={InputStyles.inputWrapper}>
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
|
@ -353,6 +348,7 @@ export default function PluginSettings() {
|
||||||
select={onStatusChange}
|
select={onStatusChange}
|
||||||
isSelected={v => v === searchValue.status}
|
isSelected={v => v === searchValue.status}
|
||||||
closeOnSelect={true}
|
closeOnSelect={true}
|
||||||
|
className={InputStyles.inputDefault}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import { showNotification } from "@api/Notifications";
|
import { showNotification } from "@api/Notifications";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { Settings, useSettings } from "@api/Settings";
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
|
import { Grid } from "@components/Grid";
|
||||||
import { Link } from "@components/Link";
|
import { Link } from "@components/Link";
|
||||||
import { authorizeCloud, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
|
import { authorizeCloud, cloudLogger, deauthorizeCloud, getCloudAuth, getCloudUrl } from "@utils/cloud";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
|
@ -85,7 +86,9 @@ function SettingsSyncSection() {
|
||||||
size={Button.Sizes.SMALL}
|
size={Button.Sizes.SMALL}
|
||||||
disabled={!sectionEnabled}
|
disabled={!sectionEnabled}
|
||||||
onClick={() => putCloudSettings(true)}
|
onClick={() => putCloudSettings(true)}
|
||||||
>Sync to Cloud</Button>
|
>
|
||||||
|
Sync to Cloud
|
||||||
|
</Button>
|
||||||
<Tooltip text="This will overwrite your local settings with the ones on the cloud. Use wisely!">
|
<Tooltip text="This will overwrite your local settings with the ones on the cloud. Use wisely!">
|
||||||
{({ onMouseLeave, onMouseEnter }) => (
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
<Button
|
<Button
|
||||||
|
@ -95,7 +98,9 @@ function SettingsSyncSection() {
|
||||||
color={Button.Colors.RED}
|
color={Button.Colors.RED}
|
||||||
disabled={!sectionEnabled}
|
disabled={!sectionEnabled}
|
||||||
onClick={() => getCloudSettings(true, true)}
|
onClick={() => getCloudSettings(true, true)}
|
||||||
>Sync from Cloud</Button>
|
>
|
||||||
|
Sync from Cloud
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Button
|
<Button
|
||||||
|
@ -103,7 +108,9 @@ function SettingsSyncSection() {
|
||||||
color={Button.Colors.RED}
|
color={Button.Colors.RED}
|
||||||
disabled={!sectionEnabled}
|
disabled={!sectionEnabled}
|
||||||
onClick={() => deleteCloudSettings()}
|
onClick={() => deleteCloudSettings()}
|
||||||
>Delete Cloud Settings</Button>
|
>
|
||||||
|
Delete Cloud Settings
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Forms.FormSection>
|
</Forms.FormSection>
|
||||||
);
|
);
|
||||||
|
@ -124,7 +131,12 @@ function CloudTab() {
|
||||||
<Switch
|
<Switch
|
||||||
key="backend"
|
key="backend"
|
||||||
value={settings.cloud.authenticated}
|
value={settings.cloud.authenticated}
|
||||||
onChange={v => { v && authorizeCloud(); if (!v) settings.cloud.authenticated = v; }}
|
onChange={v => {
|
||||||
|
if (v)
|
||||||
|
authorizeCloud();
|
||||||
|
else
|
||||||
|
settings.cloud.authenticated = v;
|
||||||
|
}}
|
||||||
note="This will request authorization if you have not yet set up cloud integrations."
|
note="This will request authorization if you have not yet set up cloud integrations."
|
||||||
>
|
>
|
||||||
Enable Cloud Integrations
|
Enable Cloud Integrations
|
||||||
|
@ -136,11 +148,27 @@ function CloudTab() {
|
||||||
<CheckedTextInput
|
<CheckedTextInput
|
||||||
key="backendUrl"
|
key="backendUrl"
|
||||||
value={settings.cloud.url}
|
value={settings.cloud.url}
|
||||||
onChange={v => { settings.cloud.url = v; settings.cloud.authenticated = false; deauthorizeCloud(); }}
|
onChange={async v => {
|
||||||
|
settings.cloud.url = v;
|
||||||
|
settings.cloud.authenticated = false;
|
||||||
|
deauthorizeCloud();
|
||||||
|
}}
|
||||||
validate={validateUrl}
|
validate={validateUrl}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Grid columns={2} gap="1em" className={Margins.top8}>
|
||||||
|
<Button
|
||||||
|
size={Button.Sizes.MEDIUM}
|
||||||
|
disabled={!settings.cloud.authenticated}
|
||||||
|
onClick={async () => {
|
||||||
|
await deauthorizeCloud();
|
||||||
|
settings.cloud.authenticated = false;
|
||||||
|
await authorizeCloud();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reauthorise
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className={Margins.top8}
|
|
||||||
size={Button.Sizes.MEDIUM}
|
size={Button.Sizes.MEDIUM}
|
||||||
color={Button.Colors.RED}
|
color={Button.Colors.RED}
|
||||||
disabled={!settings.cloud.authenticated}
|
disabled={!settings.cloud.authenticated}
|
||||||
|
@ -152,7 +180,11 @@ function CloudTab() {
|
||||||
confirmColor: "vc-cloud-erase-data-danger-btn",
|
confirmColor: "vc-cloud-erase-data-danger-btn",
|
||||||
cancelText: "Nevermind"
|
cancelText: "Nevermind"
|
||||||
})}
|
})}
|
||||||
>Erase All Data</Button>
|
>
|
||||||
|
Erase All Data
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<Forms.FormDivider className={Margins.top16} />
|
<Forms.FormDivider className={Margins.top16} />
|
||||||
</Forms.FormSection >
|
</Forms.FormSection >
|
||||||
<SettingsSyncSection />
|
<SettingsSyncSection />
|
||||||
|
|
106
src/components/VencordSettings/NotificationSettings.tsx
Normal file
106
src/components/VencordSettings/NotificationSettings.tsx
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useSettings } from "@api/Settings";
|
||||||
|
import { Margins } from "@utils/margins";
|
||||||
|
import { identity } from "@utils/misc";
|
||||||
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
|
import { Forms, Select, Slider, Text } from "@webpack/common";
|
||||||
|
|
||||||
|
import { ErrorCard } from "..";
|
||||||
|
|
||||||
|
export function NotificationSettings() {
|
||||||
|
const settings = useSettings().notifications;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ padding: "1em 0" }}>
|
||||||
|
<Forms.FormTitle tag="h5">Notification Style</Forms.FormTitle>
|
||||||
|
{settings.useNative !== "never" && Notification?.permission === "denied" && (
|
||||||
|
<ErrorCard style={{ padding: "1em" }} className={Margins.bottom8}>
|
||||||
|
<Forms.FormTitle tag="h5">Desktop Notification Permission denied</Forms.FormTitle>
|
||||||
|
<Forms.FormText>You have denied Notification Permissions. Thus, Desktop notifications will not work!</Forms.FormText>
|
||||||
|
</ErrorCard>
|
||||||
|
)}
|
||||||
|
<Forms.FormText className={Margins.bottom8}>
|
||||||
|
Some plugins may show you notifications. These come in two styles:
|
||||||
|
<ul>
|
||||||
|
<li><strong>Vencord Notifications</strong>: These are in-app notifications</li>
|
||||||
|
<li><strong>Desktop Notifications</strong>: Native Desktop notifications (like when you get a ping)</li>
|
||||||
|
</ul>
|
||||||
|
</Forms.FormText>
|
||||||
|
<Select
|
||||||
|
placeholder="Notification Style"
|
||||||
|
options={[
|
||||||
|
{ label: "Only use Desktop notifications when Discord is not focused", value: "not-focused", default: true },
|
||||||
|
{ label: "Always use Desktop notifications", value: "always" },
|
||||||
|
{ label: "Always use Vencord notifications", value: "never" },
|
||||||
|
] satisfies Array<{ value: typeof settings["useNative"]; } & Record<string, any>>}
|
||||||
|
closeOnSelect={true}
|
||||||
|
select={v => settings.useNative = v}
|
||||||
|
isSelected={v => v === settings.useNative}
|
||||||
|
serialize={identity}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Position</Forms.FormTitle>
|
||||||
|
<Select
|
||||||
|
isDisabled={settings.useNative === "always"}
|
||||||
|
placeholder="Notification Position"
|
||||||
|
options={[
|
||||||
|
{ label: "Bottom Right", value: "bottom-right", default: true },
|
||||||
|
{ label: "Top Right", value: "top-right" },
|
||||||
|
] satisfies Array<{ value: typeof settings["position"]; } & Record<string, any>>}
|
||||||
|
select={v => settings.position = v}
|
||||||
|
isSelected={v => v === settings.position}
|
||||||
|
serialize={identity}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Timeout</Forms.FormTitle>
|
||||||
|
<Forms.FormText className={Margins.bottom16}>Set to 0s to never automatically time out</Forms.FormText>
|
||||||
|
<Slider
|
||||||
|
disabled={settings.useNative === "always"}
|
||||||
|
markers={[0, 1000, 2500, 5000, 10_000, 20_000]}
|
||||||
|
minValue={0}
|
||||||
|
maxValue={20_000}
|
||||||
|
initialValue={settings.timeout}
|
||||||
|
onValueChange={v => settings.timeout = v}
|
||||||
|
onValueRender={v => (v / 1000).toFixed(2) + "s"}
|
||||||
|
onMarkerRender={v => (v / 1000) + "s"}
|
||||||
|
stickToMarkers={false}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Log Limit</Forms.FormTitle>
|
||||||
|
<Forms.FormText className={Margins.bottom16}>
|
||||||
|
The amount of notifications to save in the log until old ones are removed.
|
||||||
|
Set to <code>0</code> to disable Notification log and <code>∞</code> to never automatically remove old Notifications
|
||||||
|
</Forms.FormText>
|
||||||
|
<Slider
|
||||||
|
markers={[0, 25, 50, 75, 100, 200]}
|
||||||
|
minValue={0}
|
||||||
|
maxValue={200}
|
||||||
|
stickToMarkers={true}
|
||||||
|
initialValue={settings.logLimit}
|
||||||
|
onValueChange={v => settings.logLimit = v}
|
||||||
|
onValueRender={v => v === 200 ? "∞" : v}
|
||||||
|
onMarkerRender={v => v === 200 ? "∞" : v}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openNotificationSettingsModal() {
|
||||||
|
openModal(props => (
|
||||||
|
<ModalRoot {...props} size={ModalSize.MEDIUM}>
|
||||||
|
<ModalHeader>
|
||||||
|
<Text variant="heading-lg/semibold" style={{ flexGrow: 1 }}>Notification Settings</Text>
|
||||||
|
<ModalCloseButton onClick={props.onClose} />
|
||||||
|
</ModalHeader>
|
||||||
|
|
||||||
|
<ModalContent>
|
||||||
|
<NotificationSettings />
|
||||||
|
</ModalContent>
|
||||||
|
</ModalRoot>
|
||||||
|
));
|
||||||
|
}
|
|
@ -17,16 +17,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
||||||
import { Settings, useSettings } from "@api/Settings";
|
import { useSettings } from "@api/Settings";
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import { ErrorCard } from "@components/ErrorCard";
|
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { identity } from "@utils/misc";
|
import { identity } from "@utils/misc";
|
||||||
import { relaunch, showItemInFolder } from "@utils/native";
|
import { relaunch, showItemInFolder } from "@utils/native";
|
||||||
import { useAwaiter } from "@utils/react";
|
import { useAwaiter } from "@utils/react";
|
||||||
import { Button, Card, Forms, React, Select, Slider, Switch } from "@webpack/common";
|
import { Button, Card, Forms, React, Select, Switch, TooltipContainer } from "@webpack/common";
|
||||||
|
import { ComponentType } from "react";
|
||||||
|
|
||||||
|
import { Flex, FolderIcon, GithubIcon, LogIcon, PaintbrushIcon, RestartIcon } from "..";
|
||||||
|
import { openNotificationSettingsModal } from "./NotificationSettings";
|
||||||
import { SettingsTab, wrapTab } from "./shared";
|
import { SettingsTab, wrapTab } from "./shared";
|
||||||
|
|
||||||
const cl = classNameFactory("vc-settings-");
|
const cl = classNameFactory("vc-settings-");
|
||||||
|
@ -38,6 +41,18 @@ type KeysOfType<Object, Type> = {
|
||||||
[K in keyof Object]: Object[K] extends Type ? K : never;
|
[K in keyof Object]: Object[K] extends Type ? K : never;
|
||||||
}[keyof Object];
|
}[keyof Object];
|
||||||
|
|
||||||
|
const iconWithTooltip = (Icon: ComponentType<{ className?: string; }>, tooltip: string) => () => (
|
||||||
|
<TooltipContainer text={tooltip}>
|
||||||
|
<Icon className={cl("quick-actions-img")} />
|
||||||
|
</TooltipContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const NotificationLogIcon = iconWithTooltip(LogIcon, "Open Notification Log");
|
||||||
|
const QuickCssIcon = iconWithTooltip(PaintbrushIcon, "Edit QuickCSS");
|
||||||
|
const RelaunchIcon = iconWithTooltip(RestartIcon, "Relaunch Discord");
|
||||||
|
const OpenSettingsDirIcon = iconWithTooltip(FolderIcon, "Open Settings Directory");
|
||||||
|
const OpenGithubIcon = iconWithTooltip(GithubIcon, "View Vencord's GitHub Repository");
|
||||||
|
|
||||||
function VencordSettings() {
|
function VencordSettings() {
|
||||||
const [settingsDir, , settingsDirPending] = useAwaiter(VencordNative.settings.getSettingsDir, {
|
const [settingsDir, , settingsDirPending] = useAwaiter(VencordNative.settings.getSettingsDir, {
|
||||||
fallbackValue: "Loading..."
|
fallbackValue: "Loading..."
|
||||||
|
@ -78,7 +93,7 @@ function VencordSettings() {
|
||||||
!IS_WEB && {
|
!IS_WEB && {
|
||||||
key: "transparent",
|
key: "transparent",
|
||||||
title: "Enable window transparency.",
|
title: "Enable window transparency.",
|
||||||
note: "You need a theme that supports transparency or this will do nothing. Will stop the window from being resizable. Requires a full restart"
|
note: "You need a theme that supports transparency or this will do nothing. WILL STOP THE WINDOW FROM BEING RESIZABLE!! Requires a full restart"
|
||||||
},
|
},
|
||||||
!IS_WEB && isWindows && {
|
!IS_WEB && isWindows && {
|
||||||
key: "winCtrlQ",
|
key: "winCtrlQ",
|
||||||
|
@ -97,44 +112,59 @@ function VencordSettings() {
|
||||||
<DonateCard image={donateImage} />
|
<DonateCard image={donateImage} />
|
||||||
<Forms.FormSection title="Quick Actions">
|
<Forms.FormSection title="Quick Actions">
|
||||||
<Card className={cl("quick-actions-card")}>
|
<Card className={cl("quick-actions-card")}>
|
||||||
<React.Fragment>
|
<Button
|
||||||
|
onClick={openNotificationLogModal}
|
||||||
|
look={Button.Looks.BLANK}
|
||||||
|
>
|
||||||
|
<NotificationLogIcon />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => VencordNative.quickCss.openEditor()}
|
||||||
|
look={Button.Looks.BLANK}
|
||||||
|
>
|
||||||
|
<QuickCssIcon />
|
||||||
|
</Button>
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
onClick={relaunch}
|
onClick={relaunch}
|
||||||
size={Button.Sizes.SMALL}>
|
look={Button.Looks.BLANK}
|
||||||
Restart Client
|
>
|
||||||
|
<RelaunchIcon />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
|
||||||
onClick={() => VencordNative.quickCss.openEditor()}
|
|
||||||
size={Button.Sizes.SMALL}
|
|
||||||
disabled={settingsDir === "Loading..."}>
|
|
||||||
Open QuickCSS File
|
|
||||||
</Button>
|
|
||||||
{!IS_WEB && (
|
{!IS_WEB && (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => showItemInFolder(settingsDir)}
|
onClick={() => showItemInFolder(settingsDir)}
|
||||||
size={Button.Sizes.SMALL}
|
look={Button.Looks.BLANK}
|
||||||
disabled={settingsDirPending}>
|
disabled={settingsDirPending}
|
||||||
Open Settings Folder
|
>
|
||||||
|
<OpenSettingsDirIcon />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
onClick={() => VencordNative.native.openExternal("https://github.com/Vendicated/Vencord")}
|
onClick={() => VencordNative.native.openExternal("https://github.com/Vendicated/Vencord")}
|
||||||
size={Button.Sizes.SMALL}
|
look={Button.Looks.BLANK}
|
||||||
disabled={settingsDirPending}>
|
disabled={settingsDirPending}
|
||||||
Open in GitHub
|
>
|
||||||
|
<OpenGithubIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</React.Fragment>
|
|
||||||
</Card>
|
</Card>
|
||||||
</Forms.FormSection>
|
</Forms.FormSection>
|
||||||
|
|
||||||
<Forms.FormDivider />
|
<Forms.FormDivider />
|
||||||
|
|
||||||
<Forms.FormSection className={Margins.top16} title="Settings" tag="h5">
|
<Forms.FormSection className={Margins.top16} title="Settings" tag="h5">
|
||||||
<Forms.FormText className={Margins.bottom20}>
|
<Forms.FormText className={Margins.bottom20} style={{ color: "var(--text-muted)" }}>
|
||||||
Hint: You can change the position of this settings section in the settings of the "Settings" plugin!
|
Hint: You can change the position of this settings section in the
|
||||||
|
{" "}<Button
|
||||||
|
look={Button.Looks.BLANK}
|
||||||
|
style={{ color: "var(--text-link)", display: "inline-block" }}
|
||||||
|
onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}
|
||||||
|
>
|
||||||
|
settings of the Settings plugin
|
||||||
|
</Button>!
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
|
|
||||||
{Switches.map(s => s && (
|
{Switches.map(s => s && (
|
||||||
<Switch
|
<Switch
|
||||||
key={s.key}
|
key={s.key}
|
||||||
|
@ -212,91 +242,17 @@ function VencordSettings() {
|
||||||
serialize={identity} />
|
serialize={identity} />
|
||||||
</>}
|
</>}
|
||||||
|
|
||||||
{typeof Notification !== "undefined" && <NotificationSection settings={settings.notifications} />}
|
<Forms.FormSection className={Margins.top16} title="Vencord Notifications" tag="h5">
|
||||||
</SettingsTab>
|
<Flex>
|
||||||
);
|
<Button onClick={openNotificationSettingsModal}>
|
||||||
}
|
Notification Settings
|
||||||
|
|
||||||
function NotificationSection({ settings }: { settings: typeof Settings["notifications"]; }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Forms.FormTitle tag="h5">Notification Style</Forms.FormTitle>
|
|
||||||
{settings.useNative !== "never" && Notification?.permission === "denied" && (
|
|
||||||
<ErrorCard style={{ padding: "1em" }} className={Margins.bottom8}>
|
|
||||||
<Forms.FormTitle tag="h5">Desktop Notification Permission denied</Forms.FormTitle>
|
|
||||||
<Forms.FormText>You have denied Notification Permissions. Thus, Desktop notifications will not work!</Forms.FormText>
|
|
||||||
</ErrorCard>
|
|
||||||
)}
|
|
||||||
<Forms.FormText className={Margins.bottom8}>
|
|
||||||
Some plugins may show you notifications. These come in two styles:
|
|
||||||
<ul>
|
|
||||||
<li><strong>Vencord Notifications</strong>: These are in-app notifications</li>
|
|
||||||
<li><strong>Desktop Notifications</strong>: Native Desktop notifications (like when you get a ping)</li>
|
|
||||||
</ul>
|
|
||||||
</Forms.FormText>
|
|
||||||
<Select
|
|
||||||
placeholder="Notification Style"
|
|
||||||
options={[
|
|
||||||
{ label: "Only use Desktop notifications when Discord is not focused", value: "not-focused", default: true },
|
|
||||||
{ label: "Always use Desktop notifications", value: "always" },
|
|
||||||
{ label: "Always use Vencord notifications", value: "never" },
|
|
||||||
] satisfies Array<{ value: typeof settings["useNative"]; } & Record<string, any>>}
|
|
||||||
closeOnSelect={true}
|
|
||||||
select={v => settings.useNative = v}
|
|
||||||
isSelected={v => v === settings.useNative}
|
|
||||||
serialize={identity}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Position</Forms.FormTitle>
|
|
||||||
<Select
|
|
||||||
isDisabled={settings.useNative === "always"}
|
|
||||||
placeholder="Notification Position"
|
|
||||||
options={[
|
|
||||||
{ label: "Bottom Right", value: "bottom-right", default: true },
|
|
||||||
{ label: "Top Right", value: "top-right" },
|
|
||||||
] satisfies Array<{ value: typeof settings["position"]; } & Record<string, any>>}
|
|
||||||
select={v => settings.position = v}
|
|
||||||
isSelected={v => v === settings.position}
|
|
||||||
serialize={identity}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Timeout</Forms.FormTitle>
|
|
||||||
<Forms.FormText className={Margins.bottom16}>Set to 0s to never automatically time out</Forms.FormText>
|
|
||||||
<Slider
|
|
||||||
disabled={settings.useNative === "always"}
|
|
||||||
markers={[0, 1000, 2500, 5000, 10_000, 20_000]}
|
|
||||||
minValue={0}
|
|
||||||
maxValue={20_000}
|
|
||||||
initialValue={settings.timeout}
|
|
||||||
onValueChange={v => settings.timeout = v}
|
|
||||||
onValueRender={v => (v / 1000).toFixed(2) + "s"}
|
|
||||||
onMarkerRender={v => (v / 1000) + "s"}
|
|
||||||
stickToMarkers={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Forms.FormTitle tag="h5" className={Margins.top16 + " " + Margins.bottom8}>Notification Log Limit</Forms.FormTitle>
|
|
||||||
<Forms.FormText className={Margins.bottom16}>
|
|
||||||
The amount of notifications to save in the log until old ones are removed.
|
|
||||||
Set to <code>0</code> to disable Notification log and <code>∞</code> to never automatically remove old Notifications
|
|
||||||
</Forms.FormText>
|
|
||||||
<Slider
|
|
||||||
markers={[0, 25, 50, 75, 100, 200]}
|
|
||||||
minValue={0}
|
|
||||||
maxValue={200}
|
|
||||||
stickToMarkers={true}
|
|
||||||
initialValue={settings.logLimit}
|
|
||||||
onValueChange={v => settings.logLimit = v}
|
|
||||||
onValueRender={v => v === 200 ? "∞" : v}
|
|
||||||
onMarkerRender={v => v === 200 ? "∞" : v}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
onClick={openNotificationLogModal}
|
|
||||||
disabled={settings.logLimit === 0}
|
|
||||||
>
|
|
||||||
Open Notification Log
|
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
<Button onClick={openNotificationLogModal}>
|
||||||
|
View Notification Log
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Forms.FormSection>
|
||||||
|
</SettingsTab>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,16 +11,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-settings-quick-actions-card {
|
.vc-settings-quick-actions-card {
|
||||||
|
color: var(--text-normal);
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 1em;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
flex-grow: 1;
|
gap: 1em;
|
||||||
flex-flow: row wrap;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-settings-quick-actions-card button {
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-settings-quick-actions-img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.vc-settings-donate {
|
.vc-settings-donate {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addAccessory } from "@api/MessageAccessories";
|
import { addAccessory } from "@api/MessageAccessories";
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { getUserSettingLazy } from "@api/UserSettings";
|
import { getUserSettingLazy } from "@api/UserSettings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
|
@ -32,12 +33,12 @@ import { onlyOnce } from "@utils/onlyOnce";
|
||||||
import { makeCodeblock } from "@utils/text";
|
import { makeCodeblock } from "@utils/text";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
||||||
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Toasts, UserStore } from "@webpack/common";
|
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Text, Toasts, UserStore } from "@webpack/common";
|
||||||
|
|
||||||
import gitHash from "~git-hash";
|
import gitHash from "~git-hash";
|
||||||
import plugins, { PluginMeta } from "~plugins";
|
import plugins, { PluginMeta } from "~plugins";
|
||||||
|
|
||||||
import settings from "./settings";
|
import SettingsPlugin from "./settings";
|
||||||
|
|
||||||
const VENCORD_GUILD_ID = "1015060230222131221";
|
const VENCORD_GUILD_ID = "1015060230222131221";
|
||||||
const VENBOT_USER_ID = "1017176847865352332";
|
const VENBOT_USER_ID = "1017176847865352332";
|
||||||
|
@ -86,7 +87,7 @@ async function generateDebugInfoMessage() {
|
||||||
const info = {
|
const info = {
|
||||||
Vencord:
|
Vencord:
|
||||||
`v${VERSION} • [${gitHash}](<https://github.com/Vendicated/Vencord/commit/${gitHash}>)` +
|
`v${VERSION} • [${gitHash}](<https://github.com/Vendicated/Vencord/commit/${gitHash}>)` +
|
||||||
`${settings.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`,
|
`${SettingsPlugin.additionalInfo} - ${Intl.DateTimeFormat("en-GB", { dateStyle: "medium" }).format(BUILD_TIMESTAMP)}`,
|
||||||
Client: `${RELEASE_CHANNEL} ~ ${client}`,
|
Client: `${RELEASE_CHANNEL} ~ ${client}`,
|
||||||
Platform: window.navigator.platform
|
Platform: window.navigator.platform
|
||||||
};
|
};
|
||||||
|
@ -132,6 +133,10 @@ function generatePluginList() {
|
||||||
|
|
||||||
const checkForUpdatesOnce = onlyOnce(checkForUpdates);
|
const checkForUpdatesOnce = onlyOnce(checkForUpdates);
|
||||||
|
|
||||||
|
const settings = definePluginSettings({}).withPrivateSettings<{
|
||||||
|
dismissedDevBuildWarning?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "SupportHelper",
|
name: "SupportHelper",
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -139,6 +144,8 @@ export default definePlugin({
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"],
|
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"],
|
||||||
|
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [{
|
patches: [{
|
||||||
find: ".BEGINNING_DM.format",
|
find: ".BEGINNING_DM.format",
|
||||||
replacement: {
|
replacement: {
|
||||||
|
@ -207,17 +214,22 @@ export default definePlugin({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const repo = await VencordNative.updater.getRepo();
|
if (!IS_STANDALONE && !settings.store.dismissedDevBuildWarning) {
|
||||||
if (repo.ok && !repo.value.includes("Vendicated/Vencord")) {
|
|
||||||
return Alerts.show({
|
return Alerts.show({
|
||||||
title: "Hold on!",
|
title: "Hold on!",
|
||||||
body: <div>
|
body: <div>
|
||||||
<Forms.FormText>You are using a fork of Vencord, which we do not provide support for!</Forms.FormText>
|
<Forms.FormText>You are using a custom build of Vencord, which we do not provide support for!</Forms.FormText>
|
||||||
|
|
||||||
<Forms.FormText className={Margins.top8}>
|
<Forms.FormText className={Margins.top8}>
|
||||||
Please either switch to an <Link href="https://vencord.dev/download">officially supported version of Vencord</Link>, or
|
We only provide support for <Link href="https://vencord.dev/download">official builds</Link>.
|
||||||
contact your package maintainer for support instead.
|
Either <Link href="https://vencord.dev/download">switch to an official build</Link> or figure your issue out yourself.
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</div>
|
|
||||||
|
<Text variant="text-md/bold" className={Margins.top8}>You will be banned from receiving support if you ignore this rule.</Text>
|
||||||
|
</div>,
|
||||||
|
confirmText: "Understood",
|
||||||
|
secondaryConfirmText: "Don't show again",
|
||||||
|
onConfirmSecondary: () => settings.store.dismissedDevBuildWarning = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,50 +4,64 @@
|
||||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getCurrentChannel } from "@utils/discord";
|
import { getCurrentChannel } from "@utils/discord";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { classes } from "@utils/misc";
|
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByCode, findByProps } from "@webpack";
|
import { find, findByCode, findByProps } from "@webpack";
|
||||||
import { Heading, React, RelationshipStore, Text } from "@webpack/common";
|
import { Heading, RelationshipStore, Text } from "@webpack/common";
|
||||||
|
|
||||||
const container = findByProps("memberSinceWrapper");
|
const containerWrapper = findByProps("memberSinceWrapper");
|
||||||
|
const container = findByProps("memberSince");
|
||||||
const getCreatedAtDate = findByCode('month:"short",day:"numeric"');
|
const getCreatedAtDate = findByCode('month:"short",day:"numeric"');
|
||||||
const locale = findByProps("getLocale");
|
const locale = findByProps("getLocale");
|
||||||
const lastSection = findByProps("lastSection");
|
const lastSection = findByProps("lastSection");
|
||||||
|
const section = find((m: any) => m.section !== void 0 && Object.values(m).length === 1);
|
||||||
const cl = classNameFactory("vc-friendssince-");
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "FriendsSince",
|
name: "FriendsSince",
|
||||||
description: "Shows when you became friends with someone in the user popout",
|
description: "Shows when you became friends with someone in the user popout",
|
||||||
authors: [Devs.Elvyra],
|
authors: [Devs.Elvyra, Devs.Antti],
|
||||||
patches: [
|
patches: [
|
||||||
// User popup
|
// User popup - old layout
|
||||||
{
|
{
|
||||||
find: ".USER_PROFILE}};return",
|
find: ".USER_PROFILE}};return",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /,{userId:(\i.id).{0,30}}\)/,
|
match: /,{userId:(\i.id).{0,30}}\)/,
|
||||||
replace: "$&,$self.friendsSince({ userId: $1 })"
|
replace: "$&,$self.friendsSinceOld({ userId: $1 })"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// User DMs "User Profile" popup in the right
|
// DM User Sidebar - old layout
|
||||||
{
|
{
|
||||||
find: ".PROFILE_PANEL,",
|
find: ".PROFILE_PANEL,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /,{userId:([^,]+?)}\)/,
|
match: /,{userId:([^,]+?)}\)/,
|
||||||
replace: "$&,$self.friendsSince({ userId: $1 })"
|
replace: "$&,$self.friendsSinceOld({ userId: $1 })"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// User Profile Modal
|
// User Profile Modal - old layout
|
||||||
{
|
{
|
||||||
find: ".userInfoSectionHeader,",
|
find: ".userInfoSectionHeader,",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\.Messages\.USER_PROFILE_MEMBER_SINCE.+?userId:(.+?),textClassName:)(\i\.userInfoText)}\)/,
|
match: /(\.Messages\.USER_PROFILE_MEMBER_SINCE.+?userId:(.+?),textClassName:)(\i\.userInfoText)}\)/,
|
||||||
replace: (_, rest, userId, textClassName) => `${rest}!$self.getFriendSince(${userId}) ? ${textClassName} : void 0 }), $self.friendsSince({ userId: ${userId}, textClassName: ${textClassName} })`
|
replace: (_, rest, userId, textClassName) => `${rest}!$self.getFriendSince(${userId}) ? ${textClassName} : void 0 }), $self.friendsSinceOld({ userId: ${userId}, textClassName: ${textClassName} })`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// DM User Sidebar - new layout
|
||||||
|
{
|
||||||
|
find: ".PANEL}),nicknameIcons",
|
||||||
|
replacement: {
|
||||||
|
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id)}\)}\)/,
|
||||||
|
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:true})"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// User Profile Modal - new layout
|
||||||
|
{
|
||||||
|
find: "action:\"PRESS_APP_CONNECTION\"",
|
||||||
|
replacement: {
|
||||||
|
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
|
||||||
|
replace: "$&,$self.friendsSinceNew({userId:$1,isSidebar:false}),"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -63,7 +77,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
friendsSince: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
|
friendsSinceOld: ErrorBoundary.wrap(({ userId, textClassName }: { userId: string; textClassName?: string; }) => {
|
||||||
if (!RelationshipStore.isFriend(userId)) return null;
|
if (!RelationshipStore.isFriend(userId)) return null;
|
||||||
|
|
||||||
const friendsSince = RelationshipStore.getSince(userId);
|
const friendsSince = RelationshipStore.getSince(userId);
|
||||||
|
@ -71,11 +85,11 @@ export default definePlugin({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={lastSection.section}>
|
<div className={lastSection.section}>
|
||||||
<Heading variant="eyebrow" className={cl("title")}>
|
<Heading variant="eyebrow">
|
||||||
Friends Since
|
Friends Since
|
||||||
</Heading>
|
</Heading>
|
||||||
|
|
||||||
<div className={container.memberSinceWrapper}>
|
<div className={containerWrapper.memberSinceWrapper}>
|
||||||
{!!getCurrentChannel()?.guild_id && (
|
{!!getCurrentChannel()?.guild_id && (
|
||||||
<svg
|
<svg
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
|
@ -88,11 +102,55 @@ export default definePlugin({
|
||||||
<path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
|
<path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
<Text variant="text-sm/normal" className={classes(cl("body"), textClassName)}>
|
<Text variant="text-sm/normal" className={textClassName}>
|
||||||
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, { noop: true })
|
}, { noop: true }),
|
||||||
|
|
||||||
|
friendsSinceNew: ErrorBoundary.wrap(({ userId, isSidebar }: { userId: string; isSidebar: boolean; }) => {
|
||||||
|
if (!RelationshipStore.isFriend(userId)) return null;
|
||||||
|
|
||||||
|
const friendsSince = RelationshipStore.getSince(userId);
|
||||||
|
if (!friendsSince) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={section.section}>
|
||||||
|
<Heading variant="text-xs/semibold" style={isSidebar ? {} : { color: "var(--header-secondary)" }}>
|
||||||
|
Friends Since
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
{
|
||||||
|
isSidebar ? (
|
||||||
|
<Text variant="text-sm/normal">
|
||||||
|
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
<div className={containerWrapper.memberSinceWrapper}>
|
||||||
|
<div className={container.memberSince}>
|
||||||
|
{!!getCurrentChannel()?.guild_id && (
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="var(--interactive-normal)"
|
||||||
|
>
|
||||||
|
<path d="M13 10a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
|
||||||
|
<path d="M3 5v-.75C3 3.56 3.56 3 4.25 3s1.24.56 1.33 1.25C6.12 8.65 9.46 12 13 12h1a8 8 0 0 1 8 8 2 2 0 0 1-2 2 .21.21 0 0 1-.2-.15 7.65 7.65 0 0 0-1.32-2.3c-.15-.2-.42-.06-.39.17l.25 2c.02.15-.1.28-.25.28H9a2 2 0 0 1-2-2v-2.22c0-1.57-.67-3.05-1.53-4.37A15.85 15.85 0 0 1 3 5Z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
<Text variant="text-sm/normal">
|
||||||
|
{getCreatedAtDate(friendsSince, locale.getLocale())}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}, { noop: true }),
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
/* copy pasted from discord */
|
|
||||||
|
|
||||||
.vc-friendssince-title {
|
|
||||||
display: flex;
|
|
||||||
font-weight: 700;
|
|
||||||
margin-bottom: 6px
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-friendssince-body {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 18px
|
|
||||||
}
|
|
6
src/plugins/showAllRoles/README.md
Normal file
6
src/plugins/showAllRoles/README.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# ShowAllRoles
|
||||||
|
|
||||||
|
Display all roles on the new profiles instead of limiting them to the default two rows.
|
||||||
|
|
||||||
|
![image](https://github.com/Vendicated/Vencord/assets/71079641/3f021f03-c6f9-4fe5-83ac-a1891b5e4b37)
|
||||||
|
|
23
src/plugins/showAllRoles/index.ts
Normal file
23
src/plugins/showAllRoles/index.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2024 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "ShowAllRoles",
|
||||||
|
description: "Show all roles in new profiles.",
|
||||||
|
authors: [Devs.Luna],
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: ".Messages.VIEW_ALL_ROLES",
|
||||||
|
replacement: {
|
||||||
|
match: /return null!=\i(?=\?\i\.slice)/,
|
||||||
|
replace: "return false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
|
@ -477,12 +477,17 @@ export default definePlugin({
|
||||||
],
|
],
|
||||||
|
|
||||||
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {
|
isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {
|
||||||
|
try {
|
||||||
if (!channel) return false;
|
if (!channel) return false;
|
||||||
|
|
||||||
if (channel.channelId) channel = ChannelStore.getChannel(channel.channelId);
|
if (channel.channelId) channel = ChannelStore.getChannel(channel.channelId);
|
||||||
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;
|
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;
|
||||||
|
|
||||||
return !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || checkConnect && !PermissionStore.can(PermissionsBits.CONNECT, channel);
|
return !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || checkConnect && !PermissionStore.can(PermissionsBits.CONNECT, channel);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("[ViewHiddenChannels#isHiddenChannel]: ", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
resolveGuildChannels(channels: Record<string | number, Array<{ channel: Channel; comparator: number; }> | string | number>, shouldIncludeHidden: boolean) {
|
resolveGuildChannels(channels: Record<string | number, Array<{ channel: Channel; comparator: number; }> | string | number>, shouldIncludeHidden: boolean) {
|
||||||
|
|
|
@ -200,6 +200,34 @@ export default definePlugin({
|
||||||
match: /supports\(\i\)\{switch\(\i\)\{(case (\i).\i)/,
|
match: /supports\(\i\)\{switch\(\i\)\{(case (\i).\i)/,
|
||||||
replace: "$&.DISABLE_VIDEO:return true;$1"
|
replace: "$&.DISABLE_VIDEO:return true;$1"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.SEARCH_WITH_GOOGLE",
|
||||||
|
replacement: {
|
||||||
|
match: /\i\.isPlatformEmbedded/,
|
||||||
|
replace: "true"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: ".Messages.COPY,hint:",
|
||||||
|
replacement: [
|
||||||
|
{
|
||||||
|
match: /\i\.isPlatformEmbedded/,
|
||||||
|
replace: "true"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /\i\.\i\.copy/,
|
||||||
|
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
// Automod add filter words
|
||||||
|
{
|
||||||
|
find: '("interactionUsernameProfile',
|
||||||
|
replacement:
|
||||||
|
{
|
||||||
|
match: /\i\.isPlatformEmbedded(?=.{0,50}\.tagName)/,
|
||||||
|
replace: "true"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -528,6 +528,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
nekohaxx: {
|
nekohaxx: {
|
||||||
name: "nekohaxx",
|
name: "nekohaxx",
|
||||||
id: 1176270221628153886n
|
id: 1176270221628153886n
|
||||||
|
},
|
||||||
|
Antti: {
|
||||||
|
name: "Antti",
|
||||||
|
id: 312974985876471810n
|
||||||
}
|
}
|
||||||
} satisfies Record<string, Dev>);
|
} satisfies Record<string, Dev>);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue