mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-02-24 15:35:11 +00:00
Merge remote-tracking branch 'upstream/dev' into managed-styles-rewrite (css lag fix)
This commit is contained in:
commit
330532c0d4
116 changed files with 1074 additions and 1152 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.11.0",
|
"version": "1.11.3",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import "./ChatButton.css";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { waitFor } from "@webpack";
|
import { waitFor } from "@webpack";
|
||||||
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
import { Button, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||||
import { Channel } from "discord-types/general";
|
import { Channel } from "discord-types/general";
|
||||||
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => {
|
||||||
<Button
|
<Button
|
||||||
aria-label={props.tooltip}
|
aria-label={props.tooltip}
|
||||||
size=""
|
size=""
|
||||||
look={ButtonLooks.BLANK}
|
look={Button.Looks.BLANK}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
innerClassName={`${ButtonWrapperClasses.button} ${ChannelTextAreaClasses?.button}`}
|
innerClassName={`${ButtonWrapperClasses.button} ${ChannelTextAreaClasses?.button}`}
|
||||||
|
|
|
@ -122,7 +122,7 @@ export function findGroupChildrenByChildId(id: string | string[], children: Arra
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContextMenuProps {
|
interface ContextMenuProps {
|
||||||
contextMenuApiArguments?: Array<any>;
|
contextMenuAPIArguments?: Array<any>;
|
||||||
navId: string;
|
navId: string;
|
||||||
children: Array<ReactElement<any> | null>;
|
children: Array<ReactElement<any> | null>;
|
||||||
"aria-label": string;
|
"aria-label": string;
|
||||||
|
@ -136,7 +136,7 @@ export function _usePatchContextMenu(props: ContextMenuProps) {
|
||||||
children: cloneMenuChildren(props.children),
|
children: cloneMenuChildren(props.children),
|
||||||
};
|
};
|
||||||
|
|
||||||
props.contextMenuApiArguments ??= [];
|
props.contextMenuAPIArguments ??= [];
|
||||||
const contextMenuPatches = navPatches.get(props.navId);
|
const contextMenuPatches = navPatches.get(props.navId);
|
||||||
|
|
||||||
if (!Array.isArray(props.children)) props.children = [props.children];
|
if (!Array.isArray(props.children)) props.children = [props.children];
|
||||||
|
@ -144,7 +144,7 @@ export function _usePatchContextMenu(props: ContextMenuProps) {
|
||||||
if (contextMenuPatches) {
|
if (contextMenuPatches) {
|
||||||
for (const patch of contextMenuPatches) {
|
for (const patch of contextMenuPatches) {
|
||||||
try {
|
try {
|
||||||
patch(props.children, ...props.contextMenuApiArguments);
|
patch(props.children, ...props.contextMenuAPIArguments);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ContextMenuLogger.error(`Patch for ${props.navId} errored,`, err);
|
ContextMenuLogger.error(`Patch for ${props.navId} errored,`, err);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ export function _usePatchContextMenu(props: ContextMenuProps) {
|
||||||
|
|
||||||
for (const patch of globalPatches) {
|
for (const patch of globalPatches) {
|
||||||
try {
|
try {
|
||||||
patch(props.navId, props.children, ...props.contextMenuApiArguments);
|
patch(props.navId, props.children, ...props.contextMenuAPIArguments);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
ContextMenuLogger.error("Global patch errored,", err);
|
ContextMenuLogger.error("Global patch errored,", err);
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,6 +220,17 @@ export function migratePluginSettings(name: string, ...oldNames: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function migratePluginSetting(pluginName: string, oldSetting: string, newSetting: string) {
|
||||||
|
const settings = SettingsStore.plain.plugins[pluginName];
|
||||||
|
if (!settings) return;
|
||||||
|
|
||||||
|
if (!Object.hasOwn(settings, oldSetting) || Object.hasOwn(settings, newSetting)) return;
|
||||||
|
|
||||||
|
settings[newSetting] = settings[oldSetting];
|
||||||
|
delete settings[oldSetting];
|
||||||
|
SettingsStore.markAsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
export function definePluginSettings<
|
export function definePluginSettings<
|
||||||
Def extends SettingsDefinition,
|
Def extends SettingsDefinition,
|
||||||
Checks extends SettingsChecks<Def>,
|
Checks extends SettingsChecks<Def>,
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { Constructor } from "type-fest";
|
||||||
import { PluginMeta } from "~plugins";
|
import { PluginMeta } from "~plugins";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ISettingCustomElementProps,
|
||||||
ISettingElementProps,
|
ISettingElementProps,
|
||||||
SettingBooleanComponent,
|
SettingBooleanComponent,
|
||||||
SettingCustomComponent,
|
SettingCustomComponent,
|
||||||
|
@ -74,7 +75,7 @@ function makeDummyUser(user: { username: string; id?: string; avatar?: string; }
|
||||||
return newUser;
|
return newUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Components: Record<OptionType, React.ComponentType<ISettingElementProps<any>>> = {
|
const Components: Record<OptionType, React.ComponentType<ISettingElementProps<any> | ISettingCustomElementProps<any>>> = {
|
||||||
[OptionType.STRING]: SettingTextComponent,
|
[OptionType.STRING]: SettingTextComponent,
|
||||||
[OptionType.NUMBER]: SettingNumericComponent,
|
[OptionType.NUMBER]: SettingNumericComponent,
|
||||||
[OptionType.BIGINT]: SettingNumericComponent,
|
[OptionType.BIGINT]: SettingNumericComponent,
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
import { PluginOptionComponent } from "@utils/types";
|
import { PluginOptionComponent } from "@utils/types";
|
||||||
|
|
||||||
import { ISettingElementProps } from ".";
|
import { ISettingCustomElementProps } from ".";
|
||||||
|
|
||||||
export function SettingCustomComponent({ option, onChange, onError }: ISettingElementProps<PluginOptionComponent>) {
|
export function SettingCustomComponent({ option, onChange, onError }: ISettingCustomElementProps<PluginOptionComponent>) {
|
||||||
return option.component({ setValue: onChange, setError: onError, option });
|
return option.component({ setValue: onChange, setError: onError, option });
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import { DefinedSettings, PluginOptionBase } from "@utils/types";
|
import { DefinedSettings, PluginOptionBase } from "@utils/types";
|
||||||
|
|
||||||
export interface ISettingElementProps<T extends PluginOptionBase> {
|
interface ISettingElementPropsBase<T> {
|
||||||
option: T;
|
option: T;
|
||||||
onChange(newValue: any): void;
|
onChange(newValue: any): void;
|
||||||
pluginSettings: {
|
pluginSettings: {
|
||||||
|
@ -30,6 +30,9 @@ export interface ISettingElementProps<T extends PluginOptionBase> {
|
||||||
definedSettings?: DefinedSettings;
|
definedSettings?: DefinedSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ISettingElementProps<T extends PluginOptionBase> = ISettingElementPropsBase<T>;
|
||||||
|
export type ISettingCustomElementProps<T extends Omit<PluginOptionBase, "description" | "placeholder">> = ISettingElementPropsBase<T>;
|
||||||
|
|
||||||
export * from "../../Badge";
|
export * from "../../Badge";
|
||||||
export * from "./SettingBooleanComponent";
|
export * from "./SettingBooleanComponent";
|
||||||
export * from "./SettingCustomComponent";
|
export * from "./SettingCustomComponent";
|
||||||
|
|
|
@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) {
|
||||||
<Forms.FormText className={cl("dep-text")}>
|
<Forms.FormText className={cl("dep-text")}>
|
||||||
Restart now to apply new plugins and their settings
|
Restart now to apply new plugins and their settings
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
<Button onClick={() => location.reload()}>
|
<Button onClick={() => location.reload()} className={cl("restart-button")}>
|
||||||
Restart
|
Restart
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
@ -158,8 +158,8 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
|
||||||
className={classes(ButtonClasses.button, cl("info-button"))}
|
className={classes(ButtonClasses.button, cl("info-button"))}
|
||||||
>
|
>
|
||||||
{plugin.options && !isObjectEmpty(plugin.options)
|
{plugin.options && !isObjectEmpty(plugin.options)
|
||||||
? <CogWheel />
|
? <CogWheel className={cl("info-icon")} />
|
||||||
: <InfoIcon />}
|
: <InfoIcon className={cl("info-icon")} />}
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -63,10 +63,7 @@
|
||||||
height: 8em;
|
height: 8em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
gap: 0.25em;
|
||||||
|
|
||||||
.vc-plugins-info-card div {
|
|
||||||
line-height: 32px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-plugins-restart-card {
|
.vc-plugins-restart-card {
|
||||||
|
@ -76,11 +73,11 @@
|
||||||
color: var(--info-warning-text);
|
color: var(--info-warning-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-plugins-restart-card button {
|
.vc-plugins-restart-button {
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
background: var(--info-warning-foreground) !important;
|
background: var(--info-warning-foreground) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-plugins-info-button svg:not(:hover, :focus) {
|
.vc-plugins-info-icon:not(:hover, :focus) {
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,14 +62,21 @@ async function runReporter() {
|
||||||
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail");
|
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
let logMessage = searchType;
|
let logMessage = searchType;
|
||||||
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") {
|
||||||
else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
if (args[0].$$vencordProps != null) {
|
||||||
else if (method === "mapMangledModule") {
|
logMessage += `(${args[0].$$vencordProps.map(arg => `"${arg}"`).join(", ")})`;
|
||||||
|
} else {
|
||||||
|
logMessage += `(${args[0].toString().slice(0, 147)}...)`;
|
||||||
|
}
|
||||||
|
} else if (method === "extractAndLoadChunks") {
|
||||||
|
logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`;
|
||||||
|
} else if (method === "mapMangledModule") {
|
||||||
const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null);
|
const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null);
|
||||||
|
|
||||||
logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`;
|
logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`;
|
||||||
|
} else {
|
||||||
|
logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
||||||
}
|
}
|
||||||
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
|
|
||||||
|
|
||||||
ReporterLogger.log("Webpack Find Fail:", logMessage);
|
ReporterLogger.log("Webpack Find Fail:", logMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import { Devs } from "@utils/constants";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { isPluginDev } from "@utils/misc";
|
import { isPluginDev } from "@utils/misc";
|
||||||
import { closeModal, Modals, openModal } from "@utils/modal";
|
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Forms, Toasts, UserStore } from "@webpack/common";
|
import { Forms, Toasts, UserStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
@ -144,8 +144,8 @@ export default definePlugin({
|
||||||
closeModal(modalKey);
|
closeModal(modalKey);
|
||||||
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
|
VencordNative.native.openExternal("https://github.com/sponsors/Vendicated");
|
||||||
}}>
|
}}>
|
||||||
<Modals.ModalRoot {...props}>
|
<ModalRoot {...props}>
|
||||||
<Modals.ModalHeader>
|
<ModalHeader>
|
||||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||||
<Forms.FormTitle
|
<Forms.FormTitle
|
||||||
tag="h2"
|
tag="h2"
|
||||||
|
@ -159,8 +159,8 @@ export default definePlugin({
|
||||||
Vencord Donor
|
Vencord Donor
|
||||||
</Forms.FormTitle>
|
</Forms.FormTitle>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Modals.ModalHeader>
|
</ModalHeader>
|
||||||
<Modals.ModalContent>
|
<ModalContent>
|
||||||
<Flex>
|
<Flex>
|
||||||
<img
|
<img
|
||||||
role="presentation"
|
role="presentation"
|
||||||
|
@ -183,13 +183,13 @@ export default definePlugin({
|
||||||
Please consider supporting the development of Vencord by becoming a donor. It would mean a lot!!
|
Please consider supporting the development of Vencord by becoming a donor. It would mean a lot!!
|
||||||
</Forms.FormText>
|
</Forms.FormText>
|
||||||
</div>
|
</div>
|
||||||
</Modals.ModalContent>
|
</ModalContent>
|
||||||
<Modals.ModalFooter>
|
<ModalFooter>
|
||||||
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
<Flex style={{ width: "100%", justifyContent: "center" }}>
|
||||||
<DonateButton />
|
<DonateButton />
|
||||||
</Flex>
|
</Flex>
|
||||||
</Modals.ModalFooter>
|
</ModalFooter>
|
||||||
</Modals.ModalRoot>
|
</ModalRoot>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,11 +12,16 @@ export default definePlugin({
|
||||||
description: "API to add buttons to the chat input",
|
description: "API to add buttons to the chat input",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
|
|
||||||
patches: [{
|
patches: [
|
||||||
|
{
|
||||||
find: '"sticker")',
|
find: '"sticker")',
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /return\(!\i\.\i&&(?=\(\i\.isDM.+?(\i)\.push\(.{0,50}"gift")/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: "$&(Vencord.Api.ChatButtons._injectButtons($1,arguments[0]),true)&&"
|
match: /return\((!)?\i\.\i(?:\|\||&&)(?=\(\i\.isDM.+?(\i)\.push)/,
|
||||||
|
replace: (m, not, children) => not
|
||||||
|
? `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),true)&&`
|
||||||
|
: `${m}(Vencord.Api.ChatButtons._injectButtons(${children},arguments[0]),false)||`
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -34,12 +34,22 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".Menu,{",
|
find: "navId:",
|
||||||
all: true,
|
all: true,
|
||||||
replacement: {
|
noWarn: true,
|
||||||
match: /Menu,{(?<=\.jsxs?\)\(\i\.Menu,{)/g,
|
replacement: [
|
||||||
replace: "$&contextMenuApiArguments:typeof arguments!=='undefined'?arguments:[],"
|
{
|
||||||
|
match: /navId:(?=.+?([,}].*?\)))/g,
|
||||||
|
replace: (m, rest) => {
|
||||||
|
// Check if this navId: match is a destructuring statement, ignore it if it is
|
||||||
|
const destructuringMatch = rest.match(/}=.+/);
|
||||||
|
if (destructuringMatch == null) {
|
||||||
|
return `contextMenuAPIArguments:typeof arguments!=='undefined'?arguments:[],${m}`;
|
||||||
|
}
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
68
src/plugins/_api/menuItemDemangler.ts
Normal file
68
src/plugins/_api/menuItemDemangler.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2025 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
|
||||||
|
// duplicate values have multiple branches with different types. Just include all to be safe
|
||||||
|
const nameMap = {
|
||||||
|
radio: "MenuRadioItem",
|
||||||
|
separator: "MenuSeparator",
|
||||||
|
checkbox: "MenuCheckboxItem",
|
||||||
|
groupstart: "MenuGroup",
|
||||||
|
|
||||||
|
control: "MenuControlItem",
|
||||||
|
compositecontrol: "MenuControlItem",
|
||||||
|
|
||||||
|
item: "MenuItem",
|
||||||
|
customitem: "MenuItem",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "MenuItemDemanglerAPI",
|
||||||
|
description: "Demangles Discord's Menu Item module",
|
||||||
|
authors: [Devs.Ven],
|
||||||
|
required: true,
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '"Menu API',
|
||||||
|
replacement: {
|
||||||
|
match: /function.{0,80}type===(\i\.\i)\).{0,50}navigable:.+?Menu API/s,
|
||||||
|
replace: (m, mod) => {
|
||||||
|
const nameAssignments = [] as string[];
|
||||||
|
|
||||||
|
// if (t.type === m.MenuItem)
|
||||||
|
const typeCheckRe = canonicalizeMatch(/\(\i\.type===(\i\.\i)\)/g);
|
||||||
|
// push({type:"item"})
|
||||||
|
const pushTypeRe = /type:"(\w+)"/g;
|
||||||
|
|
||||||
|
let typeMatch: RegExpExecArray | null;
|
||||||
|
// for each if (t.type === ...)
|
||||||
|
while ((typeMatch = typeCheckRe.exec(m)) !== null) {
|
||||||
|
// extract the current menu item
|
||||||
|
const item = typeMatch[1];
|
||||||
|
// Set the starting index of the second regex to that of the first to start
|
||||||
|
// matching from after the if
|
||||||
|
pushTypeRe.lastIndex = typeCheckRe.lastIndex;
|
||||||
|
// extract the first type: "..."
|
||||||
|
const type = pushTypeRe.exec(m)?.[1];
|
||||||
|
if (type && type in nameMap) {
|
||||||
|
const name = nameMap[type];
|
||||||
|
nameAssignments.push(`Object.defineProperty(${item},"name",{value:"${name}"})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nameAssignments.length < 6) {
|
||||||
|
console.warn("[MenuItemDemanglerAPI] Expected to at least remap 6 items, only remapped", nameAssignments.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge all our redefines with the actual module
|
||||||
|
return `${nameAssignments.join(";")};${m}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
|
@ -37,12 +37,9 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".handleSendMessage,onResize",
|
find: ".handleSendMessage,onResize",
|
||||||
replacement: {
|
replacement: {
|
||||||
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
// https://regex101.com/r/hBlXpl/1
|
||||||
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
match: /let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);(?<=\)\(({.+?})\)\.then.+?)/,
|
||||||
match: /(\{openWarningPopout:.{0,100}type:this.props.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
replace: (m, parsedMessage, channel, replyOptions, extra) => m +
|
||||||
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
|
||||||
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
|
||||||
`${rest1}async ${rest2}` +
|
|
||||||
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
|
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
|
||||||
"return{shouldClear:false,shouldRefocus:true};"
|
"return{shouldClear:false,shouldRefocus:true};"
|
||||||
}
|
}
|
||||||
|
@ -52,7 +49,6 @@ export default definePlugin({
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
||||||
replace: (m, message, channel, event) =>
|
replace: (m, message, channel, event) =>
|
||||||
// the message param is shadowed by the event param, so need to alias them
|
|
||||||
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg,vcChan,${event});`
|
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg,vcChan,${event});`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,8 @@ export default definePlugin({
|
||||||
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
|
match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?(?:function\(\){return |\(\)=>))\2/,
|
||||||
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -43,8 +43,8 @@ export default definePlugin({
|
||||||
// Status emojis
|
// Status emojis
|
||||||
find: "#{intl::GUILD_OWNER}),children:",
|
find: "#{intl::GUILD_OWNER}),children:",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=\.activityEmoji,.+?animate:)\i/,
|
match: /(\.CUSTOM_STATUS.+?animate:)\i/,
|
||||||
replace: "!0"
|
replace: (_, rest) => `${rest}!0`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,14 +17,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { useStateFromStores } from "@webpack/common";
|
import { Animations, useStateFromStores } from "@webpack/common";
|
||||||
import type { CSSProperties } from "react";
|
import type { CSSProperties } from "react";
|
||||||
|
|
||||||
import { ExpandedGuildFolderStore, settings } from ".";
|
import { ExpandedGuildFolderStore, settings } from ".";
|
||||||
|
|
||||||
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
|
||||||
const Animations = findByPropsLazy("a", "animated", "useTransition");
|
|
||||||
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
const GuildsBar = findComponentByCodeLazy('("guildsnav")');
|
||||||
|
|
||||||
export default ErrorBoundary.wrap(guildsBarProps => {
|
export default ErrorBoundary.wrap(guildsBarProps => {
|
||||||
|
|
|
@ -173,8 +173,8 @@ export default definePlugin({
|
||||||
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
|
||||||
{
|
{
|
||||||
predicate: () => !settings.store.keepIcons,
|
predicate: () => !settings.store.keepIcons,
|
||||||
match: /(?<=#{intl::SERVER_FOLDER_PLACEHOLDER}.+?useTransition\)\()/,
|
match: /(?=,\{from:\{height)/,
|
||||||
replace: "$self.shouldShowTransition(arguments[0])&&"
|
replace: "&&$self.shouldShowTransition(arguments[0])"
|
||||||
},
|
},
|
||||||
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
|
||||||
{
|
{
|
||||||
|
|
|
@ -83,7 +83,7 @@ export default definePlugin({
|
||||||
if (!role) return;
|
if (!role) return;
|
||||||
|
|
||||||
if (role.colorString) {
|
if (role.colorString) {
|
||||||
children.push(
|
children.unshift(
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
id="vc-copy-role-color"
|
id="vc-copy-role-color"
|
||||||
label="Copy Role Color"
|
label="Copy Role Color"
|
||||||
|
@ -93,6 +93,20 @@ export default definePlugin({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PermissionStore.getGuildPermissionProps(guild).canManageRoles) {
|
||||||
|
children.unshift(
|
||||||
|
<Menu.MenuItem
|
||||||
|
id="vc-edit-role"
|
||||||
|
label="Edit Role"
|
||||||
|
action={async () => {
|
||||||
|
await GuildSettingsActions.open(guild.id, "ROLES");
|
||||||
|
GuildSettingsActions.selectRole(id);
|
||||||
|
}}
|
||||||
|
icon={PencilIcon}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (role.icon) {
|
if (role.icon) {
|
||||||
children.push(
|
children.push(
|
||||||
<Menu.MenuItem
|
<Menu.MenuItem
|
||||||
|
@ -110,20 +124,6 @@ export default definePlugin({
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PermissionStore.getGuildPermissionProps(guild).canManageRoles) {
|
|
||||||
children.push(
|
|
||||||
<Menu.MenuItem
|
|
||||||
id="vc-edit-role"
|
|
||||||
label="Edit Role"
|
|
||||||
action={async () => {
|
|
||||||
await GuildSettingsActions.open(guild.id, "ROLES");
|
|
||||||
GuildSettingsActions.selectRole(id);
|
|
||||||
}}
|
|
||||||
icon={PencilIcon}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { definePluginSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
|
import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
import { RenameButton } from "./components/RenameButton";
|
import { RenameButton } from "./components/RenameButton";
|
||||||
|
@ -34,7 +34,7 @@ const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open");
|
||||||
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer");
|
||||||
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
const SessionIconClasses = findByPropsLazy("sessionIcon");
|
||||||
|
|
||||||
const BlobMask = findExportedComponentLazy("BlobMask");
|
const BlobMask = findComponentByCodeLazy("!1,lowerBadgeSize:");
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
backgroundCheck: {
|
backgroundCheck: {
|
||||||
|
|
|
@ -101,8 +101,8 @@ export default definePlugin({
|
||||||
find: 'minimal:"contentColumnMinimal"',
|
find: 'minimal:"contentColumnMinimal"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\(0,\i\.useTransition\)\((\i)/,
|
match: /(?=\(0,\i\.\i\)\((\i),\{from:\{position:"absolute")/,
|
||||||
replace: "(_cb=>_cb(void 0,$1))||$&"
|
replace: "(_cb=>_cb(void 0,$1))||"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\i\.animated\.div/,
|
match: /\i\.animated\.div/,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
||||||
settings: definePluginSettings({
|
settings: definePluginSettings({
|
||||||
blurAmount: {
|
blurAmount: {
|
||||||
type: OptionType.NUMBER,
|
type: OptionType.NUMBER,
|
||||||
description: "Blur Amount",
|
description: "Blur Amount (in pixels)",
|
||||||
default: 10,
|
default: 10,
|
||||||
onChange(v) {
|
onChange(v) {
|
||||||
setStyleVariables(style, { blurAmount: v });
|
setStyleVariables(style, { blurAmount: v });
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
.vc-nsfw-img [class^="imageWrapper"] img,
|
.vc-nsfw-img [class^="imageContainer"],
|
||||||
.vc-nsfw-img [class^="wrapperPaused"] video {
|
.vc-nsfw-img [class^="wrapperPaused"] {
|
||||||
filter: blur([--blur-amount]px);
|
filter: blur([--blur-amount]px);
|
||||||
transition: filter 0.2s;
|
transition: filter 0.2s;
|
||||||
}
|
|
||||||
|
|
||||||
.vc-nsfw-img [class^="imageWrapper"]:hover img,
|
&:hover {
|
||||||
.vc-nsfw-img [class^="wrapperPaused"]:hover video {
|
filter: blur(0);
|
||||||
filter: unset;
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,29 @@
|
||||||
.client-theme-settings {
|
.vc-clientTheme-settings {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-theme-container {
|
.vc-clientTheme-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-theme-settings-labels {
|
.vc-clientTheme-labels {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-theme-container > [class^="colorSwatch"] > [class^="swatch"] {
|
.vc-clientTheme-container [class^="swatch"] {
|
||||||
border: thin solid var(--background-modifier-accent) !important;
|
border: thin solid var(--background-modifier-accent) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-theme-warning * {
|
.vc-clientTheme-warning-text {
|
||||||
color: var(--text-danger);
|
color: var(--text-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-theme-contrast-warning {
|
.vc-clientTheme-contrast-warning {
|
||||||
background-color: var(--background-primary);
|
background-color: var(--background-primary);
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
import "./clientTheme.css";
|
import "./clientTheme.css";
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
import { createStyle, deleteStyle } from "@api/Styles";
|
import { classNameFactory, createStyle, deleteStyle } from "@api/Styles";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
|
@ -15,6 +15,8 @@ import definePlugin, { OptionType, StartAt } from "@utils/types";
|
||||||
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
|
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
|
const cl = classNameFactory("vc-clientTheme-");
|
||||||
|
|
||||||
const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||||
|
|
||||||
const colorPresets = [
|
const colorPresets = [
|
||||||
|
@ -61,9 +63,9 @@ function ThemeSettings() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="client-theme-settings">
|
<div className={cl("settings")}>
|
||||||
<div className="client-theme-container">
|
<div className={cl("container")}>
|
||||||
<div className="client-theme-settings-labels">
|
<div className={cl("settings-labels")}>
|
||||||
<Forms.FormTitle tag="h3">Theme Color</Forms.FormTitle>
|
<Forms.FormTitle tag="h3">Theme Color</Forms.FormTitle>
|
||||||
<Forms.FormText>Add a color to your Discord client theme</Forms.FormText>
|
<Forms.FormText>Add a color to your Discord client theme</Forms.FormText>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,10 +79,10 @@ function ThemeSettings() {
|
||||||
{(contrastWarning || nitroThemeEnabled) && (<>
|
{(contrastWarning || nitroThemeEnabled) && (<>
|
||||||
<Forms.FormDivider className={classes(Margins.top8, Margins.bottom8)} />
|
<Forms.FormDivider className={classes(Margins.top8, Margins.bottom8)} />
|
||||||
<div className={`client-theme-contrast-warning ${contrastWarning ? (isLightTheme ? "theme-dark" : "theme-light") : ""}`}>
|
<div className={`client-theme-contrast-warning ${contrastWarning ? (isLightTheme ? "theme-dark" : "theme-light") : ""}`}>
|
||||||
<div className="client-theme-warning">
|
<div className={cl("warning")}>
|
||||||
<Forms.FormText>Warning, your theme won't look good:</Forms.FormText>
|
<Forms.FormText className={cl("warning-text")}>Warning, your theme won't look good:</Forms.FormText>
|
||||||
{contrastWarning && <Forms.FormText>Selected color won't contrast well with text</Forms.FormText>}
|
{contrastWarning && <Forms.FormText className={cl("warning-text")}>Selected color won't contrast well with text</Forms.FormText>}
|
||||||
{nitroThemeEnabled && <Forms.FormText>Nitro themes aren't supported</Forms.FormText>}
|
{nitroThemeEnabled && <Forms.FormText className={cl("warning-text")}>Nitro themes aren't supported</Forms.FormText>}
|
||||||
</div>
|
</div>
|
||||||
{(contrastWarning && fixableContrast) && <Button onClick={() => setTheme(oppositeTheme)} color={Button.Colors.RED}>Switch to {oppositeTheme} mode</Button>}
|
{(contrastWarning && fixableContrast) && <Button onClick={() => setTheme(oppositeTheme)} color={Button.Colors.RED}>Switch to {oppositeTheme} mode</Button>}
|
||||||
{(nitroThemeEnabled) && <Button onClick={() => setTheme(theme)} color={Button.Colors.RED}>Disable Nitro Theme</Button>}
|
{(nitroThemeEnabled) && <Button onClick={() => setTheme(theme)} color={Button.Colors.RED}>Disable Nitro Theme</Button>}
|
||||||
|
@ -92,15 +94,12 @@ function ThemeSettings() {
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
color: {
|
color: {
|
||||||
description: "Color your Discord client theme will be based around. Light mode isn't supported",
|
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
default: "313338",
|
default: "313338",
|
||||||
component: () => <ThemeSettings />
|
component: ThemeSettings
|
||||||
},
|
},
|
||||||
resetColor: {
|
resetColor: {
|
||||||
description: "Reset Theme Color",
|
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
default: "313338",
|
|
||||||
component: () => (
|
component: () => (
|
||||||
<Button onClick={() => onPickColor(0x313338)}>
|
<Button onClick={() => onPickColor(0x313338)}>
|
||||||
Reset Theme Color
|
Reset Theme Color
|
||||||
|
|
|
@ -69,8 +69,8 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "https://github.com/highlightjs/highlight.js/issues/2277",
|
find: "https://github.com/highlightjs/highlight.js/issues/2277",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=&&\()console.log\(`Deprecated.+?`\),/,
|
match: /\(console.log\(`Deprecated.+?`\),/,
|
||||||
replace: ""
|
replace: "("
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -95,10 +95,9 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: 'console.warn("[DEPRECATED] Please use `subscribeWithSelector` middleware");',
|
find: '"AppCrashedFatalReport: getLastCrash not supported."',
|
||||||
all: true,
|
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /console\.warn\("\[DEPRECATED\] Please use `subscribeWithSelector` middleware"\);/,
|
match: /console\.log\("AppCrashedFatalReport: getLastCrash not supported\."\);/,
|
||||||
replace: ""
|
replace: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -63,7 +63,7 @@ function makeShortcuts() {
|
||||||
default:
|
default:
|
||||||
const uniqueMatches = [...new Set(matches)];
|
const uniqueMatches = [...new Set(matches)];
|
||||||
if (uniqueMatches.length > 1)
|
if (uniqueMatches.length > 1)
|
||||||
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
|
console.warn(`Warning: This filter matches ${uniqueMatches.length} exports. Make it more specific!\n`, uniqueMatches);
|
||||||
|
|
||||||
return matches[0];
|
return matches[0];
|
||||||
}
|
}
|
||||||
|
@ -165,11 +165,38 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
|
||||||
const currentVal = val.getter();
|
const currentVal = val.getter();
|
||||||
if (!currentVal || val.preload === false) return currentVal;
|
if (!currentVal || val.preload === false) return currentVal;
|
||||||
|
|
||||||
const value = currentVal[SYM_LAZY_GET]
|
function unwrapProxy(value: any) {
|
||||||
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED]
|
if (value[SYM_LAZY_GET]) {
|
||||||
: currentVal;
|
forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED];
|
||||||
|
} else if (value.$$vencordInternal) {
|
||||||
|
return forceLoad ? value.$$vencordInternal() : value;
|
||||||
|
}
|
||||||
|
|
||||||
if (value) define(window.shortcutList, key, { value });
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = unwrapProxy(currentVal);
|
||||||
|
if (typeof value === "object" && value !== null) {
|
||||||
|
const descriptors = Object.getOwnPropertyDescriptors(value);
|
||||||
|
|
||||||
|
for (const propKey in descriptors) {
|
||||||
|
if (value[propKey] == null) continue;
|
||||||
|
|
||||||
|
const descriptor = descriptors[propKey];
|
||||||
|
if (descriptor.writable === true || descriptor.set != null) {
|
||||||
|
const currentValue = value[propKey];
|
||||||
|
const newValue = unwrapProxy(currentValue);
|
||||||
|
if (newValue != null && currentValue !== newValue) {
|
||||||
|
value[propKey] = newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value != null) {
|
||||||
|
define(window.shortcutList, key, { value });
|
||||||
|
define(window, key, { value });
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,10 +42,11 @@ export default definePlugin({
|
||||||
// Only one of the two patches will be at effect; Discord often updates to switch between them.
|
// Only one of the two patches will be at effect; Discord often updates to switch between them.
|
||||||
// See: https://discord.com/channels/1015060230222131221/1032770730703716362/1261398512017477673
|
// See: https://discord.com/channels/1015060230222131221/1032770730703716362/1261398512017477673
|
||||||
{
|
{
|
||||||
find: ".ENTER&&(!",
|
find: ".selectPreviousCommandOption(",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=(\i)\.which===\i\.\i.ENTER&&).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=&&\(\i\.preventDefault)/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: "$self.shouldSubmit($1, $2)"
|
match: /(?<=(\i)\.which(?:!==|===)\i\.\i.ENTER(\|\||&&)).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=(?:\|\||&&)\(\i\.preventDefault)/,
|
||||||
|
replace: (_, event, condition, codeblock) => `${condition === "||" ? "!" : ""}$self.shouldSubmit(${event},${codeblock})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -121,6 +121,7 @@ function DearrowButton({ component }: { component: Component<Props>; }) {
|
||||||
height="24px"
|
height="24px"
|
||||||
viewBox="0 0 36 36"
|
viewBox="0 0 36 36"
|
||||||
aria-label="Toggle Dearrow"
|
aria-label="Toggle Dearrow"
|
||||||
|
className="vc-dearrow-icon"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="#1213BD"
|
fill="#1213BD"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.vc-dearrow-toggle-off svg {
|
.vc-dearrow-toggle-off .vc-dearrow-icon {
|
||||||
filter: grayscale(1);
|
filter: grayscale(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import DecorSection from "./ui/components/DecorSection";
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
changeDecoration: {
|
changeDecoration: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "Change your avatar decoration",
|
|
||||||
component() {
|
component() {
|
||||||
if (!Vencord.Plugins.plugins.Decor.started) return <Forms.FormText>
|
if (!Vencord.Plugins.plugins.Decor.started) return <Forms.FormText>
|
||||||
Enable Decor and restart your client to change your avatar decoration.
|
Enable Decor and restart your client to change your avatar decoration.
|
||||||
|
|
|
@ -235,7 +235,7 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: ".PREMIUM_LOCKED;",
|
find: ".GUILD_SUBSCRIPTION_UNAVAILABLE;",
|
||||||
group: true,
|
group: true,
|
||||||
predicate: () => settings.store.enableEmojiBypass,
|
predicate: () => settings.store.enableEmojiBypass,
|
||||||
replacement: [
|
replacement: [
|
||||||
|
@ -256,8 +256,11 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Disallow the emoji for premium locked if the intention doesn't allow it
|
// Disallow the emoji for premium locked if the intention doesn't allow it
|
||||||
match: /!\i\.\i\.canUseEmojisEverywhere\(\i\)/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: m => `(${m}&&!${IS_BYPASSEABLE_INTENTION})`
|
match: /(!)?(\i\.\i\.canUseEmojisEverywhere\(\i\))/,
|
||||||
|
replace: (m, not) => not
|
||||||
|
? `(${m}&&!${IS_BYPASSEABLE_INTENTION})`
|
||||||
|
: `(${m}||${IS_BYPASSEABLE_INTENTION})`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Allow animated emojis to be used if the intention allows it
|
// Allow animated emojis to be used if the intention allows it
|
||||||
|
|
|
@ -22,11 +22,11 @@ import { Devs } from "@utils/constants";
|
||||||
import { getIntlMessage } from "@utils/discord";
|
import { getIntlMessage } from "@utils/discord";
|
||||||
import { NoopComponent } from "@utils/react";
|
import { NoopComponent } from "@utils/react";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { filters, findByPropsLazy, waitFor } from "@webpack";
|
import { filters, findByCodeLazy, waitFor } from "@webpack";
|
||||||
import { ChannelStore, ContextMenuApi, UserStore } from "@webpack/common";
|
import { ChannelStore, ContextMenuApi, UserStore } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
const { useMessageMenu } = findByPropsLazy("useMessageMenu");
|
const useMessageMenu = findByCodeLazy(".MESSAGE,commandTargetId:");
|
||||||
|
|
||||||
interface CopyIdMenuItemProps {
|
interface CopyIdMenuItemProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
import { UserStore, useStateFromStores } from "@webpack/common";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
||||||
|
@ -34,14 +35,19 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
UserMentionComponent: ErrorBoundary.wrap((props: UserMentionComponentProps) => (
|
UserMentionComponent: ErrorBoundary.wrap((props: UserMentionComponentProps) => {
|
||||||
<UserMentionComponent
|
const user = useStateFromStores([UserStore], () => UserStore.getUser(props.id));
|
||||||
|
if (user == null) {
|
||||||
|
return props.originalComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <UserMentionComponent
|
||||||
// This seems to be constant
|
// This seems to be constant
|
||||||
className="mention"
|
className="mention"
|
||||||
userId={props.id}
|
userId={props.id}
|
||||||
channelId={props.channelId}
|
channelId={props.channelId}
|
||||||
/>
|
/>;
|
||||||
), {
|
}, {
|
||||||
fallback: ({ wrappedProps: { originalComponent } }) => originalComponent()
|
fallback: ({ wrappedProps: { originalComponent } }) => originalComponent()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
|
||||||
import style from "./style.css?managed";
|
import style from "./style.css?managed";
|
||||||
|
|
||||||
const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:");
|
const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON");
|
||||||
|
|
||||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||||
|
|
||||||
|
|
|
@ -16,78 +16,92 @@
|
||||||
* 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 "./styles.css";
|
||||||
|
|
||||||
import { get, set } from "@api/DataStore";
|
import { get, set } from "@api/DataStore";
|
||||||
import { deleteStyle, setStyle } from "@api/Styles";
|
import { updateMessage } from "@api/MessageUpdater";
|
||||||
|
import { migratePluginSettings } from "@api/Settings";
|
||||||
import { ImageInvisible, ImageVisible } from "@components/Icons";
|
import { ImageInvisible, ImageVisible } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
|
import { classes } from "@utils/misc";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { ChannelStore } from "@webpack/common";
|
import { ChannelStore } from "@webpack/common";
|
||||||
|
import { MessageSnapshot } from "@webpack/types";
|
||||||
let style: HTMLStyleElement;
|
|
||||||
|
|
||||||
const KEY = "HideAttachments_HiddenIds";
|
const KEY = "HideAttachments_HiddenIds";
|
||||||
|
|
||||||
let hiddenMessages: Set<string> = new Set();
|
let hiddenMessages = new Set<string>();
|
||||||
const getHiddenMessages = () => get(KEY).then(set => {
|
|
||||||
hiddenMessages = set ?? new Set<string>();
|
async function getHiddenMessages() {
|
||||||
|
hiddenMessages = await get(KEY) ?? new Set();
|
||||||
return hiddenMessages;
|
return hiddenMessages;
|
||||||
});
|
}
|
||||||
|
|
||||||
const saveHiddenMessages = (ids: Set<string>) => set(KEY, ids);
|
const saveHiddenMessages = (ids: Set<string>) => set(KEY, ids);
|
||||||
|
|
||||||
|
migratePluginSettings("HideMedia", "HideAttachments");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "HideAttachments",
|
name: "HideMedia",
|
||||||
description: "Hide attachments and Embeds for individual messages via hover button",
|
description: "Hide attachments and embeds for individual messages via hover button",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
|
dependencies: ["MessageUpdaterAPI"],
|
||||||
|
|
||||||
|
patches: [{
|
||||||
|
find: "this.renderAttachments(",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\i=)this\.render(?:Attachments|Embeds|StickersAccessories)\((\i)\)/g,
|
||||||
|
replace: "$self.shouldHide($1?.id)?null:$&"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
|
||||||
renderMessagePopoverButton(msg) {
|
renderMessagePopoverButton(msg) {
|
||||||
if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length) return null;
|
// @ts-ignore - discord-types lags behind discord.
|
||||||
|
const hasAttachmentsInShapshots = msg.messageSnapshots.some(
|
||||||
|
(snapshot: MessageSnapshot) => snapshot?.message.attachments.length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!msg.attachments.length && !msg.embeds.length && !msg.stickerItems.length && !hasAttachmentsInShapshots) return null;
|
||||||
|
|
||||||
const isHidden = hiddenMessages.has(msg.id);
|
const isHidden = hiddenMessages.has(msg.id);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: isHidden ? "Show Attachments" : "Hide Attachments",
|
label: isHidden ? "Show Media" : "Hide Media",
|
||||||
icon: isHidden ? ImageVisible : ImageInvisible,
|
icon: isHidden ? ImageVisible : ImageInvisible,
|
||||||
message: msg,
|
message: msg,
|
||||||
channel: ChannelStore.getChannel(msg.channel_id),
|
channel: ChannelStore.getChannel(msg.channel_id),
|
||||||
onClick: () => this.toggleHide(msg.id)
|
onClick: () => this.toggleHide(msg.channel_id, msg.id)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderMessageAccessory({ message }) {
|
||||||
|
if (!this.shouldHide(message.id)) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={classes("vc-hideAttachments-accessory", !message.content && "vc-hideAttachments-no-content")}>
|
||||||
|
Media Hidden
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
await getHiddenMessages();
|
await getHiddenMessages();
|
||||||
await this.buildCss();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
deleteStyle("HideAttachments");
|
|
||||||
hiddenMessages.clear();
|
hiddenMessages.clear();
|
||||||
},
|
},
|
||||||
|
|
||||||
async buildCss() {
|
shouldHide(messageId: string) {
|
||||||
const elements = [...hiddenMessages].map(id => `#message-accessories-${id}`).join(",");
|
return hiddenMessages.has(messageId);
|
||||||
setStyle({
|
|
||||||
name: "HideAttachments",
|
|
||||||
source: `
|
|
||||||
:is(${elements}) :is([class*="embedWrapper"], [class*="clickableSticker"]) {
|
|
||||||
/* important is not necessary, but add it to make sure bad themes won't break it */
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
:is(${elements})::after {
|
|
||||||
content: "Attachments hidden";
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
enabled: hiddenMessages.size > 0
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async toggleHide(id: string) {
|
async toggleHide(channelId: string, messageId: string) {
|
||||||
const ids = await getHiddenMessages();
|
const ids = await getHiddenMessages();
|
||||||
if (!ids.delete(id))
|
if (!ids.delete(messageId))
|
||||||
ids.add(id);
|
ids.add(messageId);
|
||||||
|
|
||||||
await saveHiddenMessages(ids);
|
await saveHiddenMessages(ids);
|
||||||
await this.buildCss();
|
updateMessage(channelId, messageId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
10
src/plugins/hideAttachments/styles.css
Normal file
10
src/plugins/hideAttachments/styles.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
.vc-hideAttachments-accessory {
|
||||||
|
color: var(--text-muted);
|
||||||
|
margin-top: 0.5em;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-hideAttachments-no-content {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "hasFlag:{writable",
|
find: "hasFlag:{writable",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /if\((\i)<=(?:1<<30|1073741824)\)return/,
|
match: /if\((\i)<=(?:0x40000000|(?:1<<30|1073741824))\)return/,
|
||||||
replace: "if($1===(1<<20))return false;$&",
|
replace: "if($1===(1<<20))return false;$&",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -73,8 +73,6 @@ function handleActivityToggle(e: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||||
const ignoredActivityIndex = settings.store.ignoredActivities.findIndex(act => act.id === activity.id);
|
const ignoredActivityIndex = settings.store.ignoredActivities.findIndex(act => act.id === activity.id);
|
||||||
if (ignoredActivityIndex === -1) settings.store.ignoredActivities.push(activity);
|
if (ignoredActivityIndex === -1) settings.store.ignoredActivities.push(activity);
|
||||||
else settings.store.ignoredActivities.splice(ignoredActivityIndex, 1);
|
else settings.store.ignoredActivities.splice(ignoredActivityIndex, 1);
|
||||||
|
|
||||||
recalculateActivities();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function recalculateActivities() {
|
function recalculateActivities() {
|
||||||
|
@ -149,8 +147,7 @@ function IdsListComponent(props: { setValue: (value: string) => void; }) {
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
importCustomRPC: {
|
importCustomRPC: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "",
|
component: ImportCustomRPCComponent
|
||||||
component: () => <ImportCustomRPCComponent />
|
|
||||||
},
|
},
|
||||||
listMode: {
|
listMode: {
|
||||||
type: OptionType.SELECT,
|
type: OptionType.SELECT,
|
||||||
|
@ -170,7 +167,6 @@ const settings = definePluginSettings({
|
||||||
},
|
},
|
||||||
idsList: {
|
idsList: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "",
|
|
||||||
default: "",
|
default: "",
|
||||||
onChange(newValue: string) {
|
onChange(newValue: string) {
|
||||||
const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean));
|
const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean));
|
||||||
|
@ -245,7 +241,7 @@ export default definePlugin({
|
||||||
find: '"LocalActivityStore"',
|
find: '"LocalActivityStore"',
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /HANG_STATUS.+?(?=!\i\(\)\(\i,\i\)&&)(?<=(\i)\.push.+?)/,
|
match: /\.LISTENING.+?(?=!?\i\(\)\(\i,\i\))(?<=(\i)\.push.+?)/,
|
||||||
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);`
|
replace: (m, activities) => `${m}${activities}=${activities}.filter($self.isActivityNotIgnored);`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -195,6 +195,7 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<img
|
<img
|
||||||
|
className={cl("image")}
|
||||||
ref={imageRef}
|
ref={imageRef}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
|
|
|
@ -80,7 +80,12 @@ export const settings = definePluginSettings({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const imageContextMenuPatch: NavContextMenuPatchCallback = children => {
|
const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
||||||
|
// Discord re-uses the image context menu for links to for the copy and open buttons
|
||||||
|
if ("href" in props) return;
|
||||||
|
// emojis in user statuses
|
||||||
|
if (props.target?.classList?.contains("emoji")) return;
|
||||||
|
|
||||||
const { square, nearestNeighbour } = settings.use(["square", "nearestNeighbour"]);
|
const { square, nearestNeighbour } = settings.use(["square", "nearestNeighbour"]);
|
||||||
|
|
||||||
children.push(
|
children.push(
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-imgzoom-nearest-neighbor>img {
|
.vc-imgzoom-nearest-neighbor > .vc-imgzoom-image {
|
||||||
image-rendering: pixelated;
|
image-rendering: pixelated;
|
||||||
|
|
||||||
/* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
/* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */
|
||||||
|
|
|
@ -50,9 +50,9 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\(0,\i\.jsx\)\(\i\.TabBar\.Item,\{id:\i\.\i)\.BLOCKED,className:([^\s]+?)\.item,children:\i\.\i\.string\(\i\.\i#{intl::BLOCKED}\)\}\)/,
|
match: /,{id:(\i\.\i)\.BLOCKED,show:.+?className:(\i\.item)/,
|
||||||
replace: "$1.IMPLICIT,className:$2.item,children:\"Implicit\"}),$&"
|
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// Sections content
|
// Sections content
|
||||||
{
|
{
|
||||||
|
|
|
@ -142,14 +142,21 @@ for (const p of neededApiPlugins) {
|
||||||
|
|
||||||
for (const p of pluginsValues) {
|
for (const p of pluginsValues) {
|
||||||
if (p.settings) {
|
if (p.settings) {
|
||||||
p.settings.pluginName = p.name;
|
|
||||||
p.options ??= {};
|
p.options ??= {};
|
||||||
for (const [name, def] of Object.entries(p.settings.def)) {
|
|
||||||
|
p.settings.pluginName = p.name;
|
||||||
|
for (const name in p.settings.def) {
|
||||||
|
const def = p.settings.def[name];
|
||||||
const checks = p.settings.checks?.[name];
|
const checks = p.settings.checks?.[name];
|
||||||
p.options[name] = { ...def, ...checks };
|
p.options[name] = { ...def, ...checks };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (def.onChange != null) {
|
if (p.options) {
|
||||||
SettingsStore.addChangeListener(`plugins.${p.name}.${name}`, def.onChange);
|
for (const name in p.options) {
|
||||||
|
const opt = p.options[name];
|
||||||
|
if (opt.onChange != null) {
|
||||||
|
SettingsStore.addChangeListener(`plugins.${p.name}.${name}`, opt.onChange);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
src/plugins/ircColors/README.md
Normal file
17
src/plugins/ircColors/README.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# IrcColors
|
||||||
|
|
||||||
|
Makes username colors in chat unique, like in IRC clients
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Improves chat readability by assigning every user an unique nickname color,
|
||||||
|
making distinguishing between different users easier. Inspired by the feature
|
||||||
|
in many IRC clients, such as HexChat or WeeChat.
|
||||||
|
|
||||||
|
Keep in mind this overrides role colors in chat, so if you wish to know
|
||||||
|
someone's role color without checking their profile, enable the role dot: go to
|
||||||
|
**User Settings**, **Accessibility** and switch **Role Colors** to **Show role
|
||||||
|
colors next to names**.
|
||||||
|
|
||||||
|
Created for use with the [Compact++](https://gitlab.com/Grzesiek11/compactplusplus-discord-theme)
|
||||||
|
theme.
|
109
src/plugins/ircColors/index.ts
Normal file
109
src/plugins/ircColors/index.ts
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a modification for Discord's desktop app
|
||||||
|
* Copyright (c) 2023 Vendicated and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
import { hash as h64 } from "@intrnl/xxhash64";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
import { useMemo } from "@webpack/common";
|
||||||
|
|
||||||
|
// Calculate a CSS color string based on the user ID
|
||||||
|
function calculateNameColorForUser(id?: string) {
|
||||||
|
const { lightness } = settings.use(["lightness"]);
|
||||||
|
const idHash = useMemo(() => id ? h64(id) : null, [id]);
|
||||||
|
|
||||||
|
return idHash && `hsl(${idHash % 360n}, 100%, ${lightness}%)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
lightness: {
|
||||||
|
description: "Lightness, in %. Change if the colors are too light or too dark",
|
||||||
|
type: OptionType.NUMBER,
|
||||||
|
default: 70,
|
||||||
|
},
|
||||||
|
memberListColors: {
|
||||||
|
description: "Replace role colors in the member list",
|
||||||
|
restartNeeded: true,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
applyColorOnlyToUsersWithoutColor: {
|
||||||
|
description: "Apply colors only to users who don't have a predefined color",
|
||||||
|
restartNeeded: false,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
applyColorOnlyInDms: {
|
||||||
|
description: "Apply colors only in direct messages; do not apply colors in servers.",
|
||||||
|
restartNeeded: false,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default definePlugin({
|
||||||
|
name: "IrcColors",
|
||||||
|
description: "Makes username colors in chat unique, like in IRC clients",
|
||||||
|
authors: [Devs.Grzesiek11, Devs.jamesbt365],
|
||||||
|
settings,
|
||||||
|
|
||||||
|
patches: [
|
||||||
|
{
|
||||||
|
find: '="SYSTEM_TAG"',
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=className:\i\.username,style:.{0,50}:void 0,)/,
|
||||||
|
replace: "style:{color:$self.calculateNameColorForMessageContext(arguments[0])},"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
find: "#{intl::GUILD_OWNER}),children:",
|
||||||
|
replacement: {
|
||||||
|
match: /(?<=\.MEMBER_LIST}\),\[\]\),)(.+?color:)null!=.{0,50}?(?=,)/,
|
||||||
|
replace: (_, rest) => `ircColor=$self.calculateNameColorForListContext(arguments[0]),${rest}ircColor`
|
||||||
|
},
|
||||||
|
predicate: () => settings.store.memberListColors
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
calculateNameColorForMessageContext(context: any) {
|
||||||
|
const id = context?.message?.author?.id;
|
||||||
|
const colorString = context?.author?.colorString;
|
||||||
|
const color = calculateNameColorForUser(id);
|
||||||
|
|
||||||
|
if (settings.store.applyColorOnlyInDms && !context?.channel?.isPrivate()) {
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
|
||||||
|
? color
|
||||||
|
: colorString;
|
||||||
|
},
|
||||||
|
calculateNameColorForListContext(context: any) {
|
||||||
|
const id = context?.user?.id;
|
||||||
|
const colorString = context?.colorString;
|
||||||
|
const color = calculateNameColorForUser(id);
|
||||||
|
|
||||||
|
if (settings.store.applyColorOnlyInDms && !context?.channel?.isPrivate()) {
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
|
||||||
|
? color
|
||||||
|
: colorString;
|
||||||
|
}
|
||||||
|
});
|
|
@ -86,7 +86,7 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
|
||||||
|
|
||||||
const logger = new Logger("LastFMRichPresence");
|
const logger = new Logger("LastFMRichPresence");
|
||||||
|
|
||||||
const presenceStore = findByPropsLazy("getLocalPresence");
|
const PresenceStore = findByPropsLazy("getLocalPresence");
|
||||||
|
|
||||||
async function getApplicationAsset(key: string): Promise<string> {
|
async function getApplicationAsset(key: string): Promise<string> {
|
||||||
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
|
||||||
|
@ -124,6 +124,11 @@ const settings = definePluginSettings({
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
hideWithActivity: {
|
||||||
|
description: "Hide Last.fm presence if you have any other presence",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
statusName: {
|
statusName: {
|
||||||
description: "custom status text",
|
description: "custom status text",
|
||||||
type: OptionType.STRING,
|
type: OptionType.STRING,
|
||||||
|
@ -274,13 +279,17 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
async getActivity(): Promise<Activity | null> {
|
async getActivity(): Promise<Activity | null> {
|
||||||
if (settings.store.hideWithSpotify) {
|
if (settings.store.hideWithActivity) {
|
||||||
for (const activity of presenceStore.getActivities()) {
|
if (PresenceStore.getActivities().some(a => a.application_id !== applicationId)) {
|
||||||
if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) {
|
|
||||||
// there is already music status because of Spotify or richerCider (probably more)
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.store.hideWithSpotify) {
|
||||||
|
if (PresenceStore.getActivities().some(a => a.type === ActivityType.LISTENING && a.application_id !== applicationId)) {
|
||||||
|
// there is already music status because of Spotify or richerCider (probably more)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const trackData = await this.fetchTrackData();
|
const trackData = await this.fetchTrackData();
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".ROLE_MENTION)",
|
find: ".ROLE_MENTION)",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /children:\[\i&&.{0,50}\.RoleDot.{0,300},\i(?=\])/,
|
match: /children:\[\i&&.{0,100}className:\i.roleDot,.{0,200},\i(?=\])/,
|
||||||
replace: "$&,$self.renderRoleIcon(arguments[0])"
|
replace: "$&,$self.renderRoleIcon(arguments[0])"
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { isNonNullish } from "@utils/guards";
|
import { isNonNullish } from "@utils/guards";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findExportedComponentLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { SnowflakeUtils, Tooltip } from "@webpack/common";
|
import { SnowflakeUtils, Tooltip } from "@webpack/common";
|
||||||
import { Message } from "discord-types/general";
|
import { Message } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ interface Diff {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DISCORD_KT_DELAY = 1471228928;
|
const DISCORD_KT_DELAY = 1471228928;
|
||||||
const HiddenVisually = findExportedComponentLazy("HiddenVisually");
|
const HiddenVisually = findComponentByCodeLazy(".hiddenVisually]:");
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "MessageLatency",
|
name: "MessageLatency",
|
||||||
|
@ -162,7 +162,7 @@ export default definePlugin({
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</Tooltip>;
|
</Tooltip>;
|
||||||
});
|
}, { noop: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
Icon({ delta, fill, props }: {
|
Icon({ delta, fill, props }: {
|
||||||
|
|
|
@ -120,11 +120,11 @@ const settings = definePluginSettings({
|
||||||
},
|
},
|
||||||
clearMessageCache: {
|
clearMessageCache: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "Clear the linked message cache",
|
component: () => (
|
||||||
component: () =>
|
|
||||||
<Button onClick={() => messageCache.clear()}>
|
<Button onClick={() => messageCache.clear()}>
|
||||||
Clear the linked message cache
|
Clear the linked message cache
|
||||||
</Button>
|
</Button>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,8 @@
|
||||||
/* Message content highlighting */
|
.messagelogger-deleted {
|
||||||
.messagelogger-deleted [class*="contents"] > :is(div, h1, h2, h3, p) {
|
--text-normal: var(--status-danger, #f04747);
|
||||||
color: var(--status-danger, #f04747) !important;
|
--interactive-normal: var(--status-danger, #f04747);
|
||||||
}
|
--text-muted: var(--status-danger, #f04747);
|
||||||
|
--embed-title: var(--red-460, #be3535);
|
||||||
/* Markdown title highlighting */
|
--text-link: var(--red-460, #be3535);
|
||||||
.messagelogger-deleted [class*="contents"] :is(h1, h2, h3) {
|
--header-primary: var(--red-460, #be3535);
|
||||||
color: var(--status-danger, #f04747) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bot "thinking" text highlighting */
|
|
||||||
.messagelogger-deleted [class*="colorStandard"] {
|
|
||||||
color: var(--status-danger, #f04747) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Embed highlighting */
|
|
||||||
.messagelogger-deleted article :is(div, span, h1, h2, h3, p) {
|
|
||||||
color: var(--status-danger, #f04747) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messagelogger-deleted a {
|
|
||||||
color: var(--red-460, #be3535) !important;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,8 @@ export default definePlugin({
|
||||||
collapseDeleted: {
|
collapseDeleted: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
description: "Whether to collapse deleted messages, similar to blocked messages",
|
description: "Whether to collapse deleted messages, similar to blocked messages",
|
||||||
default: false
|
default: false,
|
||||||
|
restartNeeded: true,
|
||||||
},
|
},
|
||||||
logEdits: {
|
logEdits: {
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
|
@ -441,15 +442,10 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Attachment renderer
|
// Attachment renderer
|
||||||
find: ".removeMosaicItemHoverButton",
|
find: ".removeMosaicItemHoverButton",
|
||||||
group: true,
|
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(className:\i,item:\i),/,
|
match: /\[\i\.obscured\]:.+?,(?<=item:(\i).+?)/,
|
||||||
replace: "$1,item: deleted,"
|
replace: '$&"messagelogger-deleted-attachment":$1.originalItem?.deleted,'
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /\[\i\.obscured\]:.+?,/,
|
|
||||||
replace: "$& 'messagelogger-deleted-attachment': deleted,"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -500,7 +496,7 @@ export default definePlugin({
|
||||||
|
|
||||||
{
|
{
|
||||||
// Message context base menu
|
// Message context base menu
|
||||||
find: "useMessageMenu:",
|
find: ".MESSAGE,commandTargetId:",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Remove the first section if message is deleted
|
// Remove the first section if message is deleted
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
|
|
||||||
.messagelogger-deleted
|
.messagelogger-deleted
|
||||||
:is(
|
:is(
|
||||||
video,
|
.messagelogger-deleted-attachment,
|
||||||
.emoji,
|
.emoji,
|
||||||
[data-type="sticker"],
|
[data-type="sticker"],
|
||||||
iframe,
|
[class*="embedIframe"],
|
||||||
.messagelogger-deleted-attachment,
|
[class*="embedSpotify"],
|
||||||
[class|="inlineMediaEmbed"]
|
[class*="imageContainer"]
|
||||||
) {
|
) {
|
||||||
filter: grayscale(1) !important;
|
filter: grayscale(1) !important;
|
||||||
transition: 150ms filter ease-in-out;
|
transition: 150ms filter ease-in-out;
|
||||||
|
@ -17,18 +17,14 @@
|
||||||
&[class*="hiddenMosaicItem_"] {
|
&[class*="hiddenMosaicItem_"] {
|
||||||
filter: grayscale(1) blur(var(--custom-message-attachment-spoiler-blur-radius, 44px)) !important;
|
filter: grayscale(1) blur(var(--custom-message-attachment-spoiler-blur-radius, 44px)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: grayscale(0) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.messagelogger-deleted
|
.messagelogger-deleted [class*="spoilerWarning"] {
|
||||||
:is(
|
color: var(--status-danger);
|
||||||
video,
|
|
||||||
.emoji,
|
|
||||||
[data-type="sticker"],
|
|
||||||
iframe,
|
|
||||||
.messagelogger-deleted-attachment,
|
|
||||||
[class|="inlineMediaEmbed"]
|
|
||||||
):hover {
|
|
||||||
filter: grayscale(0) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-dark .messagelogger-edited {
|
.theme-dark .messagelogger-edited {
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
// TODO: Remove DataStore tags migration once enough time has passed
|
// TODO(OptionType.CUSTOM Related): Remove DataStore tags migration once enough time has passed
|
||||||
const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
|
const oldTags = await DataStore.get<Tag[]>(DATA_KEY);
|
||||||
if (oldTags != null) {
|
if (oldTags != null) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -1,372 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
|
||||||
import { Flex } from "@components/Flex";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import { getIntlMessage } from "@utils/discord";
|
|
||||||
import { Margins } from "@utils/margins";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
|
||||||
import { findByCodeLazy, findLazy } from "@webpack";
|
|
||||||
import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common";
|
|
||||||
import type { Permissions, RC } from "@webpack/types";
|
|
||||||
import type { Channel, Guild, Message, User } from "discord-types/general";
|
|
||||||
|
|
||||||
interface Tag {
|
|
||||||
// name used for identifying, must be alphanumeric + underscores
|
|
||||||
name: string;
|
|
||||||
// name shown on the tag itself, can be anything probably; automatically uppercase'd
|
|
||||||
displayName: string;
|
|
||||||
description: string;
|
|
||||||
permissions?: Permissions[];
|
|
||||||
condition?(message: Message | null, user: User, channel: Channel): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TagSetting {
|
|
||||||
text: string;
|
|
||||||
showInChat: boolean;
|
|
||||||
showInNotChat: boolean;
|
|
||||||
}
|
|
||||||
interface TagSettings {
|
|
||||||
WEBHOOK: TagSetting,
|
|
||||||
OWNER: TagSetting,
|
|
||||||
ADMINISTRATOR: TagSetting,
|
|
||||||
MODERATOR_STAFF: TagSetting,
|
|
||||||
MODERATOR: TagSetting,
|
|
||||||
VOICE_MODERATOR: TagSetting,
|
|
||||||
TRIAL_MODERATOR: TagSetting,
|
|
||||||
[k: string]: TagSetting;
|
|
||||||
}
|
|
||||||
|
|
||||||
// PermissionStore.computePermissions will not work here since it only gets permissions for the current user
|
|
||||||
const computePermissions: (options: {
|
|
||||||
user?: { id: string; } | string | null;
|
|
||||||
context?: Guild | Channel | null;
|
|
||||||
overwrites?: Channel["permissionOverwrites"] | null;
|
|
||||||
checkElevated?: boolean /* = true */;
|
|
||||||
excludeGuildPermissions?: boolean /* = false */;
|
|
||||||
}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()");
|
|
||||||
|
|
||||||
const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
|
|
||||||
|
|
||||||
const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot();
|
|
||||||
|
|
||||||
const tags: Tag[] = [
|
|
||||||
{
|
|
||||||
name: "WEBHOOK",
|
|
||||||
displayName: "Webhook",
|
|
||||||
description: "Messages sent by webhooks",
|
|
||||||
condition: isWebhook
|
|
||||||
}, {
|
|
||||||
name: "OWNER",
|
|
||||||
displayName: "Owner",
|
|
||||||
description: "Owns the server",
|
|
||||||
condition: (_, user, channel) => GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id
|
|
||||||
}, {
|
|
||||||
name: "ADMINISTRATOR",
|
|
||||||
displayName: "Admin",
|
|
||||||
description: "Has the administrator permission",
|
|
||||||
permissions: ["ADMINISTRATOR"]
|
|
||||||
}, {
|
|
||||||
name: "MODERATOR_STAFF",
|
|
||||||
displayName: "Staff",
|
|
||||||
description: "Can manage the server, channels or roles",
|
|
||||||
permissions: ["MANAGE_GUILD", "MANAGE_CHANNELS", "MANAGE_ROLES"]
|
|
||||||
}, {
|
|
||||||
name: "MODERATOR",
|
|
||||||
displayName: "Mod",
|
|
||||||
description: "Can manage messages or kick/ban people",
|
|
||||||
permissions: ["MANAGE_MESSAGES", "KICK_MEMBERS", "BAN_MEMBERS"]
|
|
||||||
}, {
|
|
||||||
name: "VOICE_MODERATOR",
|
|
||||||
displayName: "VC Mod",
|
|
||||||
description: "Can manage voice chats",
|
|
||||||
permissions: ["MOVE_MEMBERS", "MUTE_MEMBERS", "DEAFEN_MEMBERS"]
|
|
||||||
}, {
|
|
||||||
name: "CHAT_MODERATOR",
|
|
||||||
displayName: "Chat Mod",
|
|
||||||
description: "Can timeout people",
|
|
||||||
permissions: ["MODERATE_MEMBERS"]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
const defaultSettings = Object.fromEntries(
|
|
||||||
tags.map(({ name, displayName }) => [name, { text: displayName, showInChat: true, showInNotChat: true }])
|
|
||||||
) as TagSettings;
|
|
||||||
|
|
||||||
function SettingsComponent() {
|
|
||||||
const tagSettings = settings.store.tagSettings ??= defaultSettings;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Flex flexDirection="column">
|
|
||||||
{tags.map(t => (
|
|
||||||
<Card key={t.name} style={{ padding: "1em 1em 0" }}>
|
|
||||||
<Forms.FormTitle style={{ width: "fit-content" }}>
|
|
||||||
<Tooltip text={t.description}>
|
|
||||||
{({ onMouseEnter, onMouseLeave }) => (
|
|
||||||
<div
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
>
|
|
||||||
{t.displayName} Tag <Tag type={Tag.Types[t.name]} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</Forms.FormTitle>
|
|
||||||
|
|
||||||
<TextInput
|
|
||||||
type="text"
|
|
||||||
value={tagSettings[t.name]?.text ?? t.displayName}
|
|
||||||
placeholder={`Text on tag (default: ${t.displayName})`}
|
|
||||||
onChange={v => tagSettings[t.name].text = v}
|
|
||||||
className={Margins.bottom16}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
value={tagSettings[t.name]?.showInChat ?? true}
|
|
||||||
onChange={v => tagSettings[t.name].showInChat = v}
|
|
||||||
hideBorder
|
|
||||||
>
|
|
||||||
Show in messages
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
<Switch
|
|
||||||
value={tagSettings[t.name]?.showInNotChat ?? true}
|
|
||||||
onChange={v => tagSettings[t.name].showInNotChat = v}
|
|
||||||
hideBorder
|
|
||||||
>
|
|
||||||
Show in member list and profiles
|
|
||||||
</Switch>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const settings = definePluginSettings({
|
|
||||||
dontShowForBots: {
|
|
||||||
description: "Don't show extra tags for bots (excluding webhooks)",
|
|
||||||
type: OptionType.BOOLEAN
|
|
||||||
},
|
|
||||||
dontShowBotTag: {
|
|
||||||
description: "Only show extra tags for bots / Hide [BOT] text",
|
|
||||||
type: OptionType.BOOLEAN
|
|
||||||
},
|
|
||||||
tagSettings: {
|
|
||||||
type: OptionType.COMPONENT,
|
|
||||||
component: SettingsComponent,
|
|
||||||
description: "fill me"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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],
|
|
||||||
settings,
|
|
||||||
patches: [
|
|
||||||
// add tags to the tag list
|
|
||||||
{
|
|
||||||
find: ".ORIGINAL_POSTER=",
|
|
||||||
replacement: {
|
|
||||||
match: /(?=(\i)\[\i\.BOT)/,
|
|
||||||
replace: "$self.genTagTypes($1);"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "#{intl::DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL}",
|
|
||||||
replacement: [
|
|
||||||
// make the tag show the right text
|
|
||||||
{
|
|
||||||
match: /(switch\((\i)\){.+?)case (\i(?:\.\i)?)\.BOT:default:(\i)=(.{0,40}#{intl::APP_TAG}\))/,
|
|
||||||
replace: (_, origSwitch, variant, tags, displayedText, originalText) =>
|
|
||||||
`${origSwitch}default:{${displayedText} = $self.getTagText(${tags}[${variant}],${originalText})}`
|
|
||||||
},
|
|
||||||
// show OP tags correctly
|
|
||||||
{
|
|
||||||
match: /(\i)=(\i)===\i(?:\.\i)?\.ORIGINAL_POSTER/,
|
|
||||||
replace: "$1=$self.isOPTag($2)"
|
|
||||||
},
|
|
||||||
// add HTML data attributes (for easier theming)
|
|
||||||
{
|
|
||||||
match: /.botText,children:(\i)}\)]/,
|
|
||||||
replace: "$&,'data-tag':$1.toLowerCase()"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
},
|
|
||||||
// in messages
|
|
||||||
{
|
|
||||||
find: ".Types.ORIGINAL_POSTER",
|
|
||||||
replacement: {
|
|
||||||
match: /;return\((\(null==\i\?void 0:\i\.isSystemDM\(\).+?.Types.ORIGINAL_POSTER\)),null==(\i)\)/,
|
|
||||||
replace: ";$1;$2=$self.getTag({...arguments[0],origType:$2,location:'chat'});return $2 == null"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// in the member list
|
|
||||||
{
|
|
||||||
find: "#{intl::GUILD_OWNER}),children:",
|
|
||||||
replacement: {
|
|
||||||
match: /(?<type>\i)=\(null==.{0,100}\.BOT;return null!=(?<user>\i)&&\i\.bot/,
|
|
||||||
replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }); return typeof $<type> === 'number'"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// pass channel id down props to be used in profiles
|
|
||||||
{
|
|
||||||
find: ".hasAvatarForGuild(null==",
|
|
||||||
replacement: {
|
|
||||||
match: /(?=usernameIcon:)/,
|
|
||||||
replace: "moreTags_channelId:arguments[0].channelId,"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "#{intl::USER_PROFILE_PRONOUNS}",
|
|
||||||
replacement: {
|
|
||||||
match: /(?=,hideBotTag:!0)/,
|
|
||||||
replace: ",moreTags_channelId:arguments[0].moreTags_channelId"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// in profiles
|
|
||||||
{
|
|
||||||
find: ",overrideDiscriminator:",
|
|
||||||
group: true,
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
// prevent channel id from getting ghosted
|
|
||||||
// it's either this or extremely long lookbehind
|
|
||||||
match: /user:\i,nick:\i,/,
|
|
||||||
replace: "$&moreTags_channelId,"
|
|
||||||
}, {
|
|
||||||
match: /,botType:(\i),botVerified:(\i),(?!discriminatorClass:)(?<=user:(\i).+?)/g,
|
|
||||||
replace: ",botType:$self.getTag({user:$3,channelId:moreTags_channelId,origType:$1,location:'not-chat'}),botVerified:$2,"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
start() {
|
|
||||||
settings.store.tagSettings ??= defaultSettings;
|
|
||||||
|
|
||||||
// newly added field might be missing from old users
|
|
||||||
settings.store.tagSettings.CHAT_MODERATOR ??= {
|
|
||||||
text: "Chat Mod",
|
|
||||||
showInChat: true,
|
|
||||||
showInNotChat: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getPermissions(user: User, channel: Channel): string[] {
|
|
||||||
const guild = GuildStore.getGuild(channel?.guild_id);
|
|
||||||
if (!guild) return [];
|
|
||||||
|
|
||||||
const permissions = computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites });
|
|
||||||
return Object.entries(PermissionsBits)
|
|
||||||
.map(([perm, permInt]) =>
|
|
||||||
permissions & permInt ? perm : ""
|
|
||||||
)
|
|
||||||
.filter(Boolean);
|
|
||||||
},
|
|
||||||
|
|
||||||
genTagTypes(obj) {
|
|
||||||
let i = 100;
|
|
||||||
tags.forEach(({ name }) => {
|
|
||||||
obj[name] = ++i;
|
|
||||||
obj[i] = name;
|
|
||||||
obj[`${name}-BOT`] = ++i;
|
|
||||||
obj[i] = `${name}-BOT`;
|
|
||||||
obj[`${name}-OP`] = ++i;
|
|
||||||
obj[i] = `${name}-OP`;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
|
|
||||||
|
|
||||||
getTagText(passedTagName: string, originalText: string) {
|
|
||||||
try {
|
|
||||||
const [tagName, variant] = passedTagName.split("-");
|
|
||||||
if (!passedTagName) return getIntlMessage("APP_TAG");
|
|
||||||
const tag = tags.find(({ name }) => tagName === name);
|
|
||||||
if (!tag) return getIntlMessage("APP_TAG");
|
|
||||||
if (variant === "BOT" && tagName !== "WEBHOOK" && this.settings.store.dontShowForBots) return getIntlMessage("APP_TAG");
|
|
||||||
|
|
||||||
const tagText = settings.store.tagSettings?.[tag.name]?.text || tag.displayName;
|
|
||||||
switch (variant) {
|
|
||||||
case "OP":
|
|
||||||
return `${getIntlMessage("BOT_TAG_FORUM_ORIGINAL_POSTER")} • ${tagText}`;
|
|
||||||
case "BOT":
|
|
||||||
return `${getIntlMessage("APP_TAG")} • ${tagText}`;
|
|
||||||
default:
|
|
||||||
return tagText;
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return originalText;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getTag({
|
|
||||||
message, user, channelId, origType, location, channel
|
|
||||||
}: {
|
|
||||||
message?: Message,
|
|
||||||
user: User & { isClyde(): boolean; },
|
|
||||||
channel?: Channel & { isForumPost(): boolean; isMediaPost(): boolean; },
|
|
||||||
channelId?: string;
|
|
||||||
origType?: number;
|
|
||||||
location: "chat" | "not-chat";
|
|
||||||
}): number | null {
|
|
||||||
if (!user)
|
|
||||||
return null;
|
|
||||||
if (location === "chat" && user.id === "1")
|
|
||||||
return Tag.Types.OFFICIAL;
|
|
||||||
if (user.isClyde())
|
|
||||||
return Tag.Types.AI;
|
|
||||||
|
|
||||||
let type = typeof origType === "number" ? origType : null;
|
|
||||||
|
|
||||||
channel ??= ChannelStore.getChannel(channelId!) as any;
|
|
||||||
if (!channel) return type;
|
|
||||||
|
|
||||||
const settings = this.settings.store;
|
|
||||||
const perms = this.getPermissions(user, channel);
|
|
||||||
|
|
||||||
for (const tag of tags) {
|
|
||||||
if (location === "chat" && !settings.tagSettings[tag.name].showInChat) continue;
|
|
||||||
if (location === "not-chat" && !settings.tagSettings[tag.name].showInNotChat) continue;
|
|
||||||
|
|
||||||
// If the owner tag is disabled, and the user is the owner of the guild,
|
|
||||||
// avoid adding other tags because the owner will always match the condition for them
|
|
||||||
if (
|
|
||||||
tag.name !== "OWNER" &&
|
|
||||||
GuildStore.getGuild(channel?.guild_id)?.ownerId === user.id &&
|
|
||||||
(location === "chat" && !settings.tagSettings.OWNER.showInChat) ||
|
|
||||||
(location === "not-chat" && !settings.tagSettings.OWNER.showInNotChat)
|
|
||||||
) continue;
|
|
||||||
|
|
||||||
if (
|
|
||||||
tag.permissions?.some(perm => perms.includes(perm)) ||
|
|
||||||
(tag.condition?.(message!, user, channel))
|
|
||||||
) {
|
|
||||||
if ((channel.isForumPost() || channel.isMediaPost()) && channel.ownerId === user.id)
|
|
||||||
type = Tag.Types[`${tag.name}-OP`];
|
|
||||||
else if (user.bot && !isWebhook(message!, user) && !settings.dontShowBotTag)
|
|
||||||
type = Tag.Types[`${tag.name}-BOT`];
|
|
||||||
else
|
|
||||||
type = Tag.Types[tag.name];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { definePluginSettings, migratePluginSetting } from "@api/Settings";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
import { runtimeHashMessageKey } from "@utils/intlHash";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
|
@ -32,10 +32,29 @@ interface MessageDeleteProps {
|
||||||
collapsedReason: () => any;
|
collapsedReason: () => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove this migration once enough time has passed
|
||||||
|
migratePluginSetting("NoBlockedMessages", "ignoreBlockedMessages", "ignoreMessages");
|
||||||
|
const settings = definePluginSettings({
|
||||||
|
ignoreMessages: {
|
||||||
|
description: "Completely ignores incoming messages from blocked and ignored (if enabled) users",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false,
|
||||||
|
restartNeeded: true
|
||||||
|
},
|
||||||
|
applyToIgnoredUsers: {
|
||||||
|
description: "Additionally apply to 'ignored' users",
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: true,
|
||||||
|
restartNeeded: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "NoBlockedMessages",
|
name: "NoBlockedMessages",
|
||||||
description: "Hides all blocked messages from chat completely.",
|
description: "Hides all blocked/ignored messages from chat completely",
|
||||||
authors: [Devs.rushii, Devs.Samu],
|
authors: [Devs.rushii, Devs.Samu, Devs.jamesbt365],
|
||||||
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "#{intl::BLOCKED_MESSAGES_HIDE}",
|
find: "#{intl::BLOCKED_MESSAGES_HIDE}",
|
||||||
|
@ -51,38 +70,40 @@ export default definePlugin({
|
||||||
'"ReadStateStore"'
|
'"ReadStateStore"'
|
||||||
].map(find => ({
|
].map(find => ({
|
||||||
find,
|
find,
|
||||||
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
|
predicate: () => settings.store.ignoreMessages,
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(?<=function (\i)\((\i)\){)(?=.*MESSAGE_CREATE:\1)/,
|
match: /(?<=function (\i)\((\i)\){)(?=.*MESSAGE_CREATE:\1)/,
|
||||||
replace: (_, _funcName, props) => `if($self.isBlocked(${props}.message))return;`
|
replace: (_, _funcName, props) => `if($self.shouldIgnoreMessage(${props}.message))return;`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}))
|
}))
|
||||||
],
|
],
|
||||||
options: {
|
|
||||||
ignoreBlockedMessages: {
|
|
||||||
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false,
|
|
||||||
restartNeeded: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
isBlocked(message: Message) {
|
shouldIgnoreMessage(message: Message) {
|
||||||
try {
|
try {
|
||||||
return RelationshipStore.isBlocked(message.author.id);
|
if (RelationshipStore.isBlocked(message.author.id)) {
|
||||||
} catch (e) {
|
return true;
|
||||||
new Logger("NoBlockedMessages").error("Failed to check if user is blocked:", e);
|
|
||||||
}
|
}
|
||||||
},
|
return settings.store.applyToIgnoredUsers && RelationshipStore.isIgnored(message.author.id);
|
||||||
|
|
||||||
shouldHide(props: MessageDeleteProps) {
|
|
||||||
try {
|
|
||||||
return props.collapsedReason() === i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")]();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
new Logger("NoBlockedMessages").error("Failed to check if user is blocked or ignored:", e);
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldHide(props: MessageDeleteProps): boolean {
|
||||||
|
try {
|
||||||
|
const collapsedReason = props.collapsedReason();
|
||||||
|
const blockedReason = i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")]();
|
||||||
|
const ignoredReason = settings.store.applyToIgnoredUsers
|
||||||
|
? i18n.t[runtimeHashMessageKey("IGNORED_MESSAGE_COUNT")]()
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return collapsedReason === blockedReason || collapsedReason === ignoredReason;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
/*
|
|
||||||
* Vencord, a modification for Discord's desktop app
|
|
||||||
* Copyright (c) 2022 Vendicated and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getUserSettingLazy } from "@api/UserSettings";
|
|
||||||
import { Devs } from "@utils/constants";
|
|
||||||
import definePlugin from "@utils/types";
|
|
||||||
|
|
||||||
const DisableStreamPreviews = getUserSettingLazy<boolean>("voiceAndVideo", "disableStreamPreviews")!;
|
|
||||||
|
|
||||||
// @TODO: Delete this plugin in the future
|
|
||||||
export default definePlugin({
|
|
||||||
name: "NoScreensharePreview",
|
|
||||||
description: "Disables screenshare previews from being sent.",
|
|
||||||
authors: [Devs.Nuckyz],
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (!DisableStreamPreviews.getSetting()) {
|
|
||||||
DisableStreamPreviews.updateSetting(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
if (DisableStreamPreviews.getSetting()) {
|
|
||||||
DisableStreamPreviews.updateSetting(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -25,9 +25,9 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "_ensureAudio(){",
|
find: "ensureAudio(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/,
|
match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/g,
|
||||||
replace: "$self.settings.store.notificationVolume/100*"
|
replace: "$self.settings.store.notificationVolume/100*"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -100,8 +100,9 @@ export default definePlugin({
|
||||||
replace: "true"
|
replace: "true"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /!\(0,\i\.isDesktop\)\(\)/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: "false"
|
match: /(!)?\(0,\i\.isDesktop\)\(\)/,
|
||||||
|
replace: (_, not) => not ? "false" : "true"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,8 +46,9 @@ export default definePlugin({
|
||||||
find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}",
|
find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /{(\i:function\(\){return \i},?){2}}/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: m => m.replaceAll(canonicalizeMatch(/return \i/g), "return ()=>Promise.resolve(true)")
|
match: /{(?:\i:(?:function\(\){return |\(\)=>)\i}?,?){2}}/,
|
||||||
|
replace: m => m.replaceAll(canonicalizeMatch(/(function\(\){return |\(\)=>)\i/g), "$1()=>Promise.resolve(true)")
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
predicate: () => settings.store.onboarding
|
predicate: () => settings.store.onboarding
|
||||||
|
|
|
@ -157,7 +157,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||||
src={user.getAvatarURL(void 0, void 0, false)}
|
src={user.getAvatarURL(void 0, void 0, false)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Text variant="text-md/normal">
|
<Text variant="text-md/normal" className={cl("modal-list-item-text")}>
|
||||||
{
|
{
|
||||||
permission.type === PermissionType.Role
|
permission.type === PermissionType.Role
|
||||||
? role?.name ?? "Unknown Role"
|
? role?.name ?? "Unknown Role"
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
background-color: var(--background-modifier-selected);
|
background-color: var(--background-modifier-selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-permviewer-modal-list-item > div {
|
.vc-permviewer-modal-list-item-text {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal";
|
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal";
|
||||||
import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack";
|
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { Button, Forms, Text, TextInput, Toasts, useMemo, useState } from "@webpack/common";
|
import { Button, Forms, Text, TextInput, Toasts, useMemo, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { DEFAULT_COLOR, SWATCHES } from "../constants";
|
import { DEFAULT_COLOR, SWATCHES } from "../constants";
|
||||||
|
@ -30,7 +30,7 @@ interface ColorPickerWithSwatchesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
|
||||||
const ColorPickerWithSwatches = findExportedComponentLazy<ColorPickerWithSwatchesProps>("ColorPicker", "CustomColorPicker");
|
const ColorPickerWithSwatches = findComponentByCodeLazy<ColorPickerWithSwatchesProps>('id:"color-picker"');
|
||||||
|
|
||||||
export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/);
|
export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}(\i\.\i\("?.+?"?\).*?).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/);
|
||||||
|
|
||||||
|
|
|
@ -155,7 +155,7 @@ export function moveChannel(channelId: string, direction: -1 | 1) {
|
||||||
swapElementsInArray(category.channels, a, b);
|
swapElementsInArray(category.channels, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove DataStore PinnedDms migration once enough time has passed
|
// TODO(OptionType.CUSTOM Related): Remove DataStore PinnedDms migration once enough time has passed
|
||||||
async function migrateData() {
|
async function migrateData() {
|
||||||
if (Settings.plugins.PinDMs.dmSectioncollapsed != null) {
|
if (Settings.plugins.PinDMs.dmSectioncollapsed != null) {
|
||||||
settings.store.dmSectionCollapsed = Settings.plugins.PinDMs.dmSectioncollapsed;
|
settings.store.dmSectionCollapsed = Settings.plugins.PinDMs.dmSectioncollapsed;
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
import { filters, findStoreLazy, mapMangledModuleLazy } from "@webpack";
|
||||||
import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
|
import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
|
||||||
|
@ -70,7 +70,9 @@ const Icons = {
|
||||||
};
|
};
|
||||||
type Platform = keyof typeof Icons;
|
type Platform = keyof typeof Icons;
|
||||||
|
|
||||||
const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes");
|
const { useStatusFillColor } = mapMangledModuleLazy(".concat(.5625*", {
|
||||||
|
useStatusFillColor: filters.byCode(".hex")
|
||||||
|
});
|
||||||
|
|
||||||
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
|
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
|
||||||
const tooltip = platform === "embedded"
|
const tooltip = platform === "embedded"
|
||||||
|
@ -79,7 +81,7 @@ const PlatformIcon = ({ platform, status, small }: { platform: Platform, status:
|
||||||
|
|
||||||
const Icon = Icons[platform] ?? Icons.desktop;
|
const Icon = Icons[platform] ?? Icons.desktop;
|
||||||
|
|
||||||
return <Icon color={StatusUtils.useStatusFillColor(status)} tooltip={tooltip} small={small} />;
|
return <Icon color={useStatusFillColor(status)} tooltip={tooltip} small={small} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
function ensureOwnStatus(user: User) {
|
function ensureOwnStatus(user: User) {
|
||||||
|
|
|
@ -196,7 +196,7 @@ function nextReply(isUp: boolean) {
|
||||||
channel,
|
channel,
|
||||||
message,
|
message,
|
||||||
shouldMention: shouldMention(message),
|
shouldMention: shouldMention(message),
|
||||||
showMentionToggle: channel.isPrivate() && message.author.id !== meId,
|
showMentionToggle: !channel.isPrivate() && message.author.id !== meId,
|
||||||
_isQuickReply: true
|
_isQuickReply: true
|
||||||
});
|
});
|
||||||
ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS");
|
ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS");
|
||||||
|
|
|
@ -7,15 +7,12 @@
|
||||||
import { DataStore } from "@api/index";
|
import { DataStore } from "@api/index";
|
||||||
import { Logger } from "@utils/Logger";
|
import { Logger } from "@utils/Logger";
|
||||||
import { openModal } from "@utils/modal";
|
import { openModal } from "@utils/modal";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common";
|
||||||
import { showToast, Toasts, UserStore } from "@webpack/common";
|
|
||||||
|
|
||||||
import { ReviewDBAuth } from "./entities";
|
import { ReviewDBAuth } from "./entities";
|
||||||
|
|
||||||
const DATA_STORE_KEY = "rdb-auth";
|
const DATA_STORE_KEY = "rdb-auth";
|
||||||
|
|
||||||
const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal");
|
|
||||||
|
|
||||||
export let Auth: ReviewDBAuth = {};
|
export let Auth: ReviewDBAuth = {};
|
||||||
|
|
||||||
export async function initAuth() {
|
export async function initAuth() {
|
||||||
|
|
|
@ -39,7 +39,7 @@ function BlockedUser({ user, isBusy, setIsBusy }: { user: ReviewDBUser; isBusy:
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cl("block-modal-row")}>
|
<div className={cl("block-modal-row")}>
|
||||||
<img src={user.profilePhoto} alt="" />
|
<img className={cl("block-modal-avatar")} src={user.profilePhoto} alt="" />
|
||||||
<Forms.FormText className={cl("block-modal-username")}>{user.username}</Forms.FormText>
|
<Forms.FormText className={cl("block-modal-username")}>{user.username}</Forms.FormText>
|
||||||
<UnblockButton
|
<UnblockButton
|
||||||
onClick={isBusy ? undefined : async () => {
|
onClick={isBusy ? undefined : async () => {
|
||||||
|
|
|
@ -65,7 +65,7 @@ function Modal({ modalProps, modalKey, discordId, name, type }: { modalProps: an
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ModalFooter className={cl("modal-footer")}>
|
<ModalFooter className={cl("modal-footer")}>
|
||||||
<div>
|
<div className={cl("modal-footer-wrapper")}>
|
||||||
{ownReview && (
|
{ownReview && (
|
||||||
<ReviewComponent
|
<ReviewComponent
|
||||||
refetch={refetch}
|
refetch={refetch}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import { cl } from "./utils";
|
||||||
export const settings = definePluginSettings({
|
export const settings = definePluginSettings({
|
||||||
authorize: {
|
authorize: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "Authorize with ReviewDB",
|
|
||||||
component: () => (
|
component: () => (
|
||||||
<Button onClick={() => authorize()}>
|
<Button onClick={() => authorize()}>
|
||||||
Authorize with ReviewDB
|
Authorize with ReviewDB
|
||||||
|
@ -56,7 +55,6 @@ export const settings = definePluginSettings({
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "ReviewDB buttons",
|
|
||||||
component: () => (
|
component: () => (
|
||||||
<div className={cl("button-grid")} >
|
<div className={cl("button-grid")} >
|
||||||
<Button onClick={openBlockModal}>Manage Blocked Users</Button>
|
<Button onClick={openBlockModal}>Manage Blocked Users</Button>
|
||||||
|
|
|
@ -16,16 +16,11 @@
|
||||||
border: 1px solid var(--profile-message-input-border-color);
|
border: 1px solid var(--profile-message-input-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-modal-footer > div {
|
.vc-rdb-modal-footer-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 6px 16px;
|
margin: 6px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When input becomes disabled(while sending review), input adds unneccesary padding to left, this prevents it */
|
|
||||||
.vc-rdb-input > div > div {
|
|
||||||
padding-left: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-rdb-placeholder {
|
.vc-rdb-placeholder {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -69,7 +64,7 @@
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-review-comment img {
|
.vc-rdb-review-comment [class*="avatar"] {
|
||||||
vertical-align: text-top;
|
vertical-align: text-top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,13 +112,13 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-block-modal-row img {
|
.vc-rdb-block-modal-avatar {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
height: 2em;
|
height: 2em;
|
||||||
width: 2em;
|
width: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-rdb-block-modal img::before {
|
.vc-rdb-block-modal-avatar::before {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -68,15 +68,16 @@ function PickerModal({ rootProps, close }: { rootProps: ModalProps, close(): voi
|
||||||
return (
|
return (
|
||||||
<ModalRoot {...rootProps}>
|
<ModalRoot {...rootProps}>
|
||||||
<ModalHeader className={cl("modal-header")}>
|
<ModalHeader className={cl("modal-header")}>
|
||||||
<Forms.FormTitle tag="h2">
|
<Forms.FormTitle tag="h2" className={cl("modal-title")}>
|
||||||
Timestamp Picker
|
Timestamp Picker
|
||||||
</Forms.FormTitle>
|
</Forms.FormTitle>
|
||||||
|
|
||||||
<ModalCloseButton onClick={close} />
|
<ModalCloseButton onClick={close} className={cl("modal-close-button")} />
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
|
|
||||||
<ModalContent className={cl("modal-content")}>
|
<ModalContent className={cl("modal-content")}>
|
||||||
<input
|
<input
|
||||||
|
className={cl("date-picker")}
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={e => setValue(e.currentTarget.value)}
|
onChange={e => setValue(e.currentTarget.value)}
|
||||||
|
@ -86,6 +87,7 @@ function PickerModal({ rootProps, close }: { rootProps: ModalProps, close(): voi
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Forms.FormTitle>Timestamp Format</Forms.FormTitle>
|
<Forms.FormTitle>Timestamp Format</Forms.FormTitle>
|
||||||
|
<div className={cl("format-select")}>
|
||||||
<Select
|
<Select
|
||||||
options={
|
options={
|
||||||
Formats.map(m => ({
|
Formats.map(m => ({
|
||||||
|
@ -103,6 +105,7 @@ function PickerModal({ rootProps, close }: { rootProps: ModalProps, close(): voi
|
||||||
)}
|
)}
|
||||||
renderOptionValue={() => rendered}
|
renderOptionValue={() => rendered}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Forms.FormTitle className={Margins.bottom8}>Preview</Forms.FormTitle>
|
<Forms.FormTitle className={Margins.bottom8}>Preview</Forms.FormTitle>
|
||||||
<Forms.FormText className={cl("preview-text")}>
|
<Forms.FormText className={cl("preview-text")}>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.vc-st-modal-content input {
|
.vc-st-date-picker {
|
||||||
background-color: var(--input-background);
|
background-color: var(--input-background);
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
width: 95%;
|
width: 95%;
|
||||||
|
@ -12,35 +12,28 @@
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-format-label,
|
.vc-st-format-select {
|
||||||
.vc-st-format-label span {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-st-modal-content [class|="select"] {
|
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
--background-modifier-accent: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-content [class|="select"] span {
|
.vc-st-format-label {
|
||||||
background-color: var(--input-background);
|
--background-modifier-accent: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-header {
|
.vc-st-modal-header {
|
||||||
place-content: center space-between;
|
place-content: center space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-header h1 {
|
.vc-st-modal-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-modal-header button {
|
.vc-st-modal-close-button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-preview-text {
|
.vc-st-preview-text {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-st-button svg {
|
|
||||||
transform: scale(1.1) translateY(1px);
|
|
||||||
}
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ export function openGuildInfoModal(guild: Guild) {
|
||||||
const enum Tabs {
|
const enum Tabs {
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
Friends,
|
Friends,
|
||||||
BlockedUsers
|
BlockedUsers,
|
||||||
|
IgnoredUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GuildProps {
|
interface GuildProps {
|
||||||
|
@ -44,7 +45,8 @@ interface RelationshipProps extends GuildProps {
|
||||||
|
|
||||||
const fetched = {
|
const fetched = {
|
||||||
friends: false,
|
friends: false,
|
||||||
blocked: false
|
blocked: false,
|
||||||
|
ignored: false
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderTimestamp(timestamp: number) {
|
function renderTimestamp(timestamp: number) {
|
||||||
|
@ -56,10 +58,12 @@ function renderTimestamp(timestamp: number) {
|
||||||
function GuildInfoModal({ guild }: GuildProps) {
|
function GuildInfoModal({ guild }: GuildProps) {
|
||||||
const [friendCount, setFriendCount] = useState<number>();
|
const [friendCount, setFriendCount] = useState<number>();
|
||||||
const [blockedCount, setBlockedCount] = useState<number>();
|
const [blockedCount, setBlockedCount] = useState<number>();
|
||||||
|
const [ignoredCount, setIgnoredCount] = useState<number>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetched.friends = false;
|
fetched.friends = false;
|
||||||
fetched.blocked = false;
|
fetched.blocked = false;
|
||||||
|
fetched.ignored = false;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
|
const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo);
|
||||||
|
@ -90,6 +94,7 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
<div className={cl("header")}>
|
<div className={cl("header")}>
|
||||||
{iconUrl
|
{iconUrl
|
||||||
? <img
|
? <img
|
||||||
|
className={cl("icon")}
|
||||||
src={iconUrl}
|
src={iconUrl}
|
||||||
alt=""
|
alt=""
|
||||||
onClick={() => openImageModal({
|
onClick={() => openImageModal({
|
||||||
|
@ -132,12 +137,19 @@ function GuildInfoModal({ guild }: GuildProps) {
|
||||||
>
|
>
|
||||||
Blocked Users{blockedCount !== undefined ? ` (${blockedCount})` : ""}
|
Blocked Users{blockedCount !== undefined ? ` (${blockedCount})` : ""}
|
||||||
</TabBar.Item>
|
</TabBar.Item>
|
||||||
|
<TabBar.Item
|
||||||
|
className={cl("tab", { selected: currentTab === Tabs.IgnoredUsers })}
|
||||||
|
id={Tabs.IgnoredUsers}
|
||||||
|
>
|
||||||
|
Ignored Users{ignoredCount !== undefined ? ` (${ignoredCount})` : ""}
|
||||||
|
</TabBar.Item>
|
||||||
</TabBar>
|
</TabBar>
|
||||||
|
|
||||||
<div className={cl("tab-content")}>
|
<div className={cl("tab-content")}>
|
||||||
{currentTab === Tabs.ServerInfo && <ServerInfoTab guild={guild} />}
|
{currentTab === Tabs.ServerInfo && <ServerInfoTab guild={guild} />}
|
||||||
{currentTab === Tabs.Friends && <FriendsTab guild={guild} setCount={setFriendCount} />}
|
{currentTab === Tabs.Friends && <FriendsTab guild={guild} setCount={setFriendCount} />}
|
||||||
{currentTab === Tabs.BlockedUsers && <BlockedUsersTab guild={guild} setCount={setBlockedCount} />}
|
{currentTab === Tabs.BlockedUsers && <BlockedUsersTab guild={guild} setCount={setBlockedCount} />}
|
||||||
|
{currentTab === Tabs.IgnoredUsers && <IgnoredUserTab guild={guild} setCount={setIgnoredCount} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -159,6 +171,7 @@ function Owner(guildId: string, owner: User) {
|
||||||
return (
|
return (
|
||||||
<div className={cl("owner")}>
|
<div className={cl("owner")}>
|
||||||
<img
|
<img
|
||||||
|
className={cl("owner-avatar")}
|
||||||
src={ownerAvatarUrl}
|
src={ownerAvatarUrl}
|
||||||
alt=""
|
alt=""
|
||||||
onClick={() => openImageModal({
|
onClick={() => openImageModal({
|
||||||
|
@ -211,7 +224,13 @@ function BlockedUsersTab({ guild, setCount }: RelationshipProps) {
|
||||||
return UserList("blocked", guild, blockedIds, setCount);
|
return UserList("blocked", guild, blockedIds, setCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setCount: (count: number) => void) {
|
function IgnoredUserTab({ guild, setCount }: RelationshipProps) {
|
||||||
|
const ignoredIds = Object.keys(RelationshipStore.getRelationships()).filter(id => RelationshipStore.isIgnored(id));
|
||||||
|
return UserList("ignored", guild, ignoredIds, setCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function UserList(type: "friends" | "blocked" | "ignored", guild: Guild, ids: string[], setCount: (count: number) => void) {
|
||||||
const missing = [] as string[];
|
const missing = [] as string[];
|
||||||
const members = [] as string[];
|
const members = [] as string[];
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-gp-header img {
|
.vc-gp-icon {
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
gap: 0.2em;
|
gap: 0.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-gp-owner img {
|
.vc-gp-owner-avatar {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -84,9 +84,9 @@ export const Code = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeTableRows = lines.map((line, i) => (
|
const codeTableRows = lines.map((line, i) => (
|
||||||
<tr key={i}>
|
<tr className={cl("table-row")} key={i}>
|
||||||
<td style={{ color: theme.plainColor }}>{i + 1}</td>
|
<td className={cl("table-cell")} style={{ color: theme.plainColor }}>{i + 1}</td>
|
||||||
<td>{line}</td>
|
<td className={cl("table-cell")}>{line}</td>
|
||||||
</tr>
|
</tr>
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ export const Highlighter = ({
|
||||||
color: themeBase.plainColor,
|
color: themeBase.plainColor,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<code>
|
<code className={cl("code")}>
|
||||||
<Header
|
<Header
|
||||||
langName={langName}
|
langName={langName}
|
||||||
useDevIcon={useDevIcon}
|
useDevIcon={useDevIcon}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
.shiki-container {
|
.vc-shiki-container {
|
||||||
border: 4px;
|
border: 4px;
|
||||||
background-color: var(--background-secondary);
|
background-color: var(--background-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root {
|
.vc-shiki-root {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root code {
|
.vc-shiki-root .vc-shiki-code {
|
||||||
display: block;
|
display: block;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding: 0.5em;
|
padding: 0.5em;
|
||||||
|
@ -20,16 +20,16 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-devicon {
|
.vc-shiki-devicon {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-plain code {
|
.vc-shiki-plain .vc-shiki-code {
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-btns {
|
.vc-shiki-btns {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -37,25 +37,25 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root:hover .shiki-btns {
|
.vc-shiki-root:hover .vc-shiki-btns {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-btn {
|
.vc-shiki-btn {
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-btn ~ .shiki-btn {
|
.vc-shiki-btn ~ .vc-shiki-btn {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-btn:last-child {
|
.vc-shiki-btn:last-child {
|
||||||
border-radius: 4px 0;
|
border-radius: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-spinner-container {
|
.vc-shiki-spinner-container {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: rgb(0 0 0 / 60%);
|
background-color: rgb(0 0 0 / 60%);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -64,11 +64,11 @@
|
||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-preview {
|
.vc-shiki-preview {
|
||||||
margin-bottom: 2em;
|
margin-bottom: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-lang {
|
.vc-shiki-lang {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -77,24 +77,24 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-table {
|
.vc-shiki-table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-table tr {
|
.vc-shiki-table-row {
|
||||||
height: 19px;
|
height: 19px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root td:first-child {
|
.vc-shiki-root .vc-shiki-table-cell:first-child {
|
||||||
border-right: 1px solid transparent;
|
border-right: 1px solid transparent;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shiki-root td:last-child {
|
.vc-shiki-root .vc-shiki-table-cell:last-child {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { resolveLang } from "../api/languages";
|
||||||
import { HighlighterProps } from "../components/Highlighter";
|
import { HighlighterProps } from "../components/Highlighter";
|
||||||
import { HljsSetting } from "../types";
|
import { HljsSetting } from "../types";
|
||||||
|
|
||||||
export const cl = classNameFactory("shiki-");
|
export const cl = classNameFactory("vc-shiki-");
|
||||||
|
|
||||||
export const shouldUseHljs = ({
|
export const shouldUseHljs = ({
|
||||||
lang,
|
lang,
|
||||||
|
|
|
@ -33,6 +33,7 @@ export function VerifiedIcon() {
|
||||||
forcedIconColor={forcedIconColor}
|
forcedIconColor={forcedIconColor}
|
||||||
size={16}
|
size={16}
|
||||||
tooltipText={getIntlMessage("CONNECTION_VERIFIED")}
|
tooltipText={getIntlMessage("CONNECTION_VERIFIED")}
|
||||||
|
className="vc-sc-tooltip-icon"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect
|
||||||
<span className="vc-sc-tooltip">
|
<span className="vc-sc-tooltip">
|
||||||
<span className="vc-sc-connection-name">{connection.name}</span>
|
<span className="vc-sc-connection-name">{connection.name}</span>
|
||||||
{connection.verified && <VerifiedIcon />}
|
{connection.verified && <VerifiedIcon />}
|
||||||
<TooltipIcon height={16} width={16} />
|
<TooltipIcon height={16} width={16} className="vc-sc-tooltip-icon" />
|
||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
key={connection.id}
|
key={connection.id}
|
||||||
|
|
|
@ -14,6 +14,6 @@
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-sc-tooltip svg {
|
.vc-sc-tooltip-icon {
|
||||||
min-width: 16px;
|
min-width: 16px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
import { classes } from "@utils/misc";
|
||||||
import { formatDuration } from "@utils/text";
|
import { formatDuration } from "@utils/text";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
|
||||||
|
@ -25,7 +26,7 @@ import type { Channel } from "discord-types/general";
|
||||||
|
|
||||||
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
|
import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission } from "../../permissionsViewer/components/RolesAndUsersPermissions";
|
||||||
import { sortPermissionOverwrites } from "../../permissionsViewer/utils";
|
import { sortPermissionOverwrites } from "../../permissionsViewer/utils";
|
||||||
import { settings } from "..";
|
import { cl, settings } from "..";
|
||||||
|
|
||||||
const enum SortOrderTypes {
|
const enum SortOrderTypes {
|
||||||
LATEST_ACTIVITY = 0,
|
LATEST_ACTIVITY = 0,
|
||||||
|
@ -168,19 +169,19 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
}, [channelId]);
|
}, [channelId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ChatScrollClasses.auto + " " + ChatScrollClasses.customTheme + " " + ChatClasses.chatContent + " " + "shc-lock-screen-outer-container"}>
|
<div className={classes(ChatScrollClasses.auto, ChatScrollClasses.customTheme, ChatScrollClasses.managedReactiveScroller)}>
|
||||||
<div className="shc-lock-screen-container">
|
<div className={cl("container")}>
|
||||||
<img className="shc-lock-screen-logo" src={HiddenChannelLogo} />
|
<img className={cl("logo")} src={HiddenChannelLogo} />
|
||||||
|
|
||||||
<div className="shc-lock-screen-heading-container">
|
<div className={cl("heading-container")}>
|
||||||
<Text variant="heading-xxl/bold">This is a {!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) ? "hidden" : "locked"} {ChannelTypesToChannelNames[type]} channel.</Text>
|
<Text variant="heading-xxl/bold">This is a {!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) ? "hidden" : "locked"} {ChannelTypesToChannelNames[type]} channel</Text>
|
||||||
{channel.isNSFW() &&
|
{channel.isNSFW() &&
|
||||||
<Tooltip text="NSFW">
|
<Tooltip text="NSFW">
|
||||||
{({ onMouseLeave, onMouseEnter }) => (
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
<svg
|
<svg
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
className="shc-lock-screen-heading-nsfw-icon"
|
className={cl("heading-nsfw-icon")}
|
||||||
width="32"
|
width="32"
|
||||||
height="32"
|
height="32"
|
||||||
viewBox="0 0 48 48"
|
viewBox="0 0 48 48"
|
||||||
|
@ -202,7 +203,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{channel.isForumChannel() && topic && topic.length > 0 && (
|
{channel.isForumChannel() && topic && topic.length > 0 && (
|
||||||
<div className="shc-lock-screen-topic-container">
|
<div className={cl("topic-container")}>
|
||||||
{Parser.parseTopic(topic, false, { channelId })}
|
{Parser.parseTopic(topic, false, { channelId })}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -213,7 +214,6 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
<Timestamp timestamp={new Date(SnowflakeUtils.extractTimestamp(lastMessageId))} />
|
<Timestamp timestamp={new Date(SnowflakeUtils.extractTimestamp(lastMessageId))} />
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
|
|
||||||
{lastPinTimestamp &&
|
{lastPinTimestamp &&
|
||||||
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={new Date(lastPinTimestamp)} /></Text>
|
<Text variant="text-md/normal">Last message pin: <Timestamp timestamp={new Date(lastPinTimestamp)} /></Text>
|
||||||
}
|
}
|
||||||
|
@ -247,7 +247,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
<Text variant="text-md/normal">Default sort order: {SortOrderTypesToNames[defaultSortOrder]}</Text>
|
<Text variant="text-md/normal">Default sort order: {SortOrderTypesToNames[defaultSortOrder]}</Text>
|
||||||
}
|
}
|
||||||
{defaultReactionEmoji != null &&
|
{defaultReactionEmoji != null &&
|
||||||
<div className="shc-lock-screen-default-emoji-container">
|
<div className={cl("default-emoji-container")}>
|
||||||
<Text variant="text-md/normal">Default reaction emoji:</Text>
|
<Text variant="text-md/normal">Default reaction emoji:</Text>
|
||||||
{Parser.defaultRules[defaultReactionEmoji.emojiName ? "emoji" : "customEmoji"].react({
|
{Parser.defaultRules[defaultReactionEmoji.emojiName ? "emoji" : "customEmoji"].react({
|
||||||
name: defaultReactionEmoji.emojiName
|
name: defaultReactionEmoji.emojiName
|
||||||
|
@ -258,29 +258,29 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
src: defaultReactionEmoji.emojiName
|
src: defaultReactionEmoji.emojiName
|
||||||
? EmojiUtils.getURL(defaultReactionEmoji.emojiName)
|
? EmojiUtils.getURL(defaultReactionEmoji.emojiName)
|
||||||
: void 0
|
: void 0
|
||||||
}, void 0, { key: "0" })}
|
}, void 0, { key: 0 })}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
{channel.hasFlag(ChannelFlags.REQUIRE_TAG) &&
|
{channel.hasFlag(ChannelFlags.REQUIRE_TAG) &&
|
||||||
<Text variant="text-md/normal">Posts on this forum require a tag to be set.</Text>
|
<Text variant="text-md/normal">Posts on this forum require a tag to be set.</Text>
|
||||||
}
|
}
|
||||||
{availableTags && availableTags.length > 0 &&
|
{availableTags && availableTags.length > 0 &&
|
||||||
<div className="shc-lock-screen-tags-container">
|
<div className={cl("tags-container")}>
|
||||||
<Text variant="text-lg/bold">Available tags:</Text>
|
<Text variant="text-lg/bold">Available tags:</Text>
|
||||||
<div className="shc-lock-screen-tags">
|
<div className={cl("tags")}>
|
||||||
{availableTags.map(tag => <TagComponent tag={tag} key={tag.id} />)}
|
{availableTags.map(tag => <TagComponent tag={tag} key={tag.id} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div className="shc-lock-screen-allowed-users-and-roles-container">
|
<div className={cl("allowed-users-and-roles-container")}>
|
||||||
<div className="shc-lock-screen-allowed-users-and-roles-container-title">
|
<div className={cl("allowed-users-and-roles-container-title")}>
|
||||||
{Settings.plugins.PermissionsViewer.enabled && (
|
{Vencord.Plugins.isPluginEnabled("PermissionsViewer") && (
|
||||||
<Tooltip text="Permission Details">
|
<Tooltip text="Permission Details">
|
||||||
{({ onMouseLeave, onMouseEnter }) => (
|
{({ onMouseLeave, onMouseEnter }) => (
|
||||||
<button
|
<button
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
className="shc-lock-screen-allowed-users-and-roles-container-permdetails-btn"
|
className={cl("allowed-users-and-roles-container-permdetails-btn")}
|
||||||
onClick={() => openRolesAndUsersPermissionsModal(permissions, GuildStore.getGuild(channel.guild_id), channel.name)}
|
onClick={() => openRolesAndUsersPermissionsModal(permissions, GuildStore.getGuild(channel.guild_id), channel.name)}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -300,7 +300,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
||||||
<button
|
<button
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
className="shc-lock-screen-allowed-users-and-roles-container-toggle-btn"
|
className={cl("allowed-users-and-roles-container-toggle-btn")}
|
||||||
onClick={() => settings.store.defaultAllowedUsersAndRolesDropdownState = !defaultAllowedUsersAndRolesDropdownState}
|
onClick={() => settings.store.defaultAllowedUsersAndRolesDropdownState = !defaultAllowedUsersAndRolesDropdownState}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
|
|
@ -19,8 +19,10 @@
|
||||||
import "./style.css";
|
import "./style.css";
|
||||||
|
|
||||||
import { definePluginSettings } from "@api/Settings";
|
import { definePluginSettings } from "@api/Settings";
|
||||||
|
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 { classes } from "@utils/misc";
|
||||||
import { canonicalizeMatch } from "@utils/patches";
|
import { canonicalizeMatch } from "@utils/patches";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findByPropsLazy } from "@webpack";
|
import { findByPropsLazy } from "@webpack";
|
||||||
|
@ -31,6 +33,8 @@ import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen";
|
||||||
|
|
||||||
const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread", "icon");
|
const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread", "icon");
|
||||||
|
|
||||||
|
export const cl = classNameFactory("vc-shc-");
|
||||||
|
|
||||||
const enum ShowMode {
|
const enum ShowMode {
|
||||||
LockIcon,
|
LockIcon,
|
||||||
HiddenIconWithMutedStyle
|
HiddenIconWithMutedStyle
|
||||||
|
@ -108,8 +112,11 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Prevent Discord from trying to connect to hidden voice channels
|
// Prevent Discord from trying to connect to hidden voice channels
|
||||||
match: /(?=&&\i\.\i\.selectVoiceChannel\((\i)\.id\))/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: (_, channel) => `&&!$self.isHiddenChannel(${channel})`
|
match: /(?=(\|\||&&)\i\.\i\.selectVoiceChannel\((\i)\.id\))/,
|
||||||
|
replace: (_, condition, channel) => condition === "||"
|
||||||
|
? `||$self.isHiddenChannel(${channel})`
|
||||||
|
: `&&!$self.isHiddenChannel(${channel})`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Make Discord show inside the channel if clicking on a hidden or locked channel
|
// Make Discord show inside the channel if clicking on a hidden or locked channel
|
||||||
|
@ -122,8 +129,11 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".AUDIENCE),{isSubscriptionGated",
|
find: ".AUDIENCE),{isSubscriptionGated",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /!(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: (m, channel) => `${m}&&!$self.isHiddenChannel(${channel})`
|
match: /(!)?(\i)\.isRoleSubscriptionTemplatePreviewChannel\(\)/,
|
||||||
|
replace: (m, not, channel) => not
|
||||||
|
? `${m}&&!$self.isHiddenChannel(${channel})`
|
||||||
|
: `${m}||$self.isHiddenChannel(${channel})`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -173,8 +183,11 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// Make voice channels also appear as muted if they are muted
|
// Make voice channels also appear as muted if they are muted
|
||||||
{
|
{
|
||||||
match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)if\((\i)\)return (\i\.MUTED);/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: (_, otherClasses, isMuted, mutedClassExpression) => `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return "";`
|
match: /(?<=\.wrapper:\i\.notInteractive,)(.+?)(if\()?(\i)(?:\)return |\?)(\i\.MUTED)/,
|
||||||
|
replace: (_, otherClasses, isIf, isMuted, mutedClassExpression) => isIf
|
||||||
|
? `${isMuted}?${mutedClassExpression}:"",${otherClasses}if(${isMuted})return ""`
|
||||||
|
: `${isMuted}?${mutedClassExpression}:"",${otherClasses}${isMuted}?""`
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -184,8 +197,9 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden
|
// Make muted channels also appear as unread if hide unreads is false, using the HiddenIconWithMutedStyle and the channel is hidden
|
||||||
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
predicate: () => settings.store.hideUnreads === false && settings.store.showMode === ShowMode.HiddenIconWithMutedStyle,
|
||||||
match: /\.LOCKED;if\((?<={channel:(\i).+?)/,
|
// FIXME(Bundler change related): Remove old compatiblity once enough time has passed
|
||||||
replace: (m, channel) => `${m}!$self.isHiddenChannel(${channel})&&`
|
match: /(?<=\.LOCKED(?:;if\(|:))(?<={channel:(\i).+?)/,
|
||||||
|
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Hide unreads
|
// Hide unreads
|
||||||
|
@ -539,7 +553,7 @@ export default definePlugin({
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
role="img"
|
role="img"
|
||||||
>
|
>
|
||||||
<path className="shc-evenodd-fill-current-color" d="M17 11V7C17 4.243 14.756 2 12 2C9.242 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" />
|
<path fill="currentcolor" fillRule="evenodd" d="M17 11V7C17 4.243 14.756 2 12 2C9.242 2 7 4.243 7 7V11C5.897 11 5 11.896 5 13V20C5 21.103 5.897 22 7 22H17C18.103 22 19 21.103 19 20V13C19 11.896 18.103 11 17 11ZM12 18C11.172 18 10.5 17.328 10.5 16.5C10.5 15.672 11.172 15 12 15C12.828 15 13.5 15.672 13.5 16.5C13.5 17.328 12.828 18 12 18ZM15 11H9V7C9 5.346 10.346 4 12 4C13.654 4 15 5.346 15 7V11Z" />
|
||||||
</svg>
|
</svg>
|
||||||
), { noop: true }),
|
), { noop: true }),
|
||||||
|
|
||||||
|
@ -549,14 +563,14 @@ export default definePlugin({
|
||||||
<svg
|
<svg
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
className={ChannelListClasses.icon + " " + "shc-hidden-channel-icon"}
|
className={classes(ChannelListClasses.icon, cl("hidden-channel-icon"))}
|
||||||
width="24"
|
width="24"
|
||||||
height="24"
|
height="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
role="img"
|
role="img"
|
||||||
>
|
>
|
||||||
<path className="shc-evenodd-fill-current-color" d="m19.8 22.6-4.2-4.15q-.875.275-1.762.413Q12.95 19 12 19q-3.775 0-6.725-2.087Q2.325 14.825 1 11.5q.525-1.325 1.325-2.463Q3.125 7.9 4.15 7L1.4 4.2l1.4-1.4 18.4 18.4ZM12 16q.275 0 .512-.025.238-.025.513-.1l-5.4-5.4q-.075.275-.1.513-.025.237-.025.512 0 1.875 1.312 3.188Q10.125 16 12 16Zm7.3.45-3.175-3.15q.175-.425.275-.862.1-.438.1-.938 0-1.875-1.312-3.188Q13.875 7 12 7q-.5 0-.938.1-.437.1-.862.3L7.65 4.85q1.025-.425 2.1-.638Q10.825 4 12 4q3.775 0 6.725 2.087Q21.675 8.175 23 11.5q-.575 1.475-1.512 2.738Q20.55 15.5 19.3 16.45Zm-4.625-4.6-3-3q.7-.125 1.288.112.587.238 1.012.688.425.45.613 1.038.187.587.087 1.162Z" />
|
<path fill="currentcolor" fillRule="evenodd" d="m19.8 22.6-4.2-4.15q-.875.275-1.762.413Q12.95 19 12 19q-3.775 0-6.725-2.087Q2.325 14.825 1 11.5q.525-1.325 1.325-2.463Q3.125 7.9 4.15 7L1.4 4.2l1.4-1.4 18.4 18.4ZM12 16q.275 0 .512-.025.238-.025.513-.1l-5.4-5.4q-.075.275-.1.513-.025.237-.025.512 0 1.875 1.312 3.188Q10.125 16 12 16Zm7.3.45-3.175-3.15q.175-.425.275-.862.1-.438.1-.938 0-1.875-1.312-3.188Q13.875 7 12 7q-.5 0-.938.1-.437.1-.862.3L7.65 4.85q1.025-.425 2.1-.638Q10.825 4 12 4q3.775 0 6.725 2.087Q21.675 8.175 23 11.5q-.575 1.475-1.512 2.738Q20.55 15.5 19.3 16.45Zm-4.625-4.6-3-3q.7-.125 1.288.112.587.238 1.012.688.425.45.613 1.038.187.587.087 1.162Z" />
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
@ -1,43 +1,31 @@
|
||||||
.shc-lock-screen-outer-container {
|
.vc-shc-container {
|
||||||
overflow: hidden scroll;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
gap: 0.65em;
|
||||||
|
margin: 0.5em 0;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-container > * {
|
.vc-shc-logo {
|
||||||
margin: 5px;
|
width: 12em;
|
||||||
|
height: 12em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-logo {
|
.vc-shc-heading-container {
|
||||||
width: 180px;
|
|
||||||
height: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-heading-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-heading-container > * {
|
.vc-shc-heading-nsfw-icon {
|
||||||
margin: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-heading-nsfw-icon {
|
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-topic-container {
|
.vc-shc-topic-container {
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
background: var(--bg-overlay-3, var(--background-secondary));
|
background: var(--bg-overlay-3, var(--background-secondary));
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
@ -45,91 +33,75 @@
|
||||||
max-width: 70vw;
|
max-width: 70vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-tags-container {
|
.vc-shc-default-emoji-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--bg-overlay-3, var(--background-secondary));
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.75em;
|
||||||
|
margin-left: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-shc-tags-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
background: var(--bg-overlay-3, var(--background-secondary));
|
background: var(--bg-overlay-3, var(--background-secondary));
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
padding: 0.75em;
|
||||||
|
gap: 0.75em;
|
||||||
max-width: 70vw;
|
max-width: 70vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-tags-container > * {
|
.vc-shc-tags {
|
||||||
margin: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-tags {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 0.35em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-evenodd-fill-current-color {
|
.vc-shc-allowed-users-and-roles-container {
|
||||||
fill-rule: evenodd;
|
|
||||||
fill: currentcolor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-hidden-channel-icon {
|
|
||||||
margin-left: 6px;
|
|
||||||
z-index: 0;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-default-emoji-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-default-emoji-container > [class^="emojiContainer"] {
|
|
||||||
background: var(--bg-overlay-3, var(--background-secondary));
|
|
||||||
border-radius: 8px;
|
|
||||||
padding: 5px 6px;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--bg-overlay-3, var(--background-secondary));
|
background: var(--bg-overlay-3, var(--background-secondary));
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
padding: 10px;
|
padding: 0.75em;
|
||||||
max-width: 70vw;
|
max-width: 70vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container-title {
|
.vc-shc-allowed-users-and-roles-container-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container-toggle-btn {
|
.vc-shc-allowed-users-and-roles-container-toggle-btn {
|
||||||
all: unset;
|
all: unset;
|
||||||
margin-left: 5px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container-toggle-btn > svg {
|
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container-permdetails-btn {
|
.vc-shc-allowed-users-and-roles-container-permdetails-btn {
|
||||||
all: unset;
|
all: unset;
|
||||||
margin-right: 5px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container-permdetails-btn > svg {
|
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shc-lock-screen-allowed-users-and-roles-container > [class^="members"] {
|
.vc-shc-allowed-users-and-roles-container > [class^="members"] {
|
||||||
margin-left: 10px;
|
margin-left: 12px;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vc-shc-hidden-channel-icon {
|
||||||
|
cursor: not-allowed;
|
||||||
|
margin-left: 6px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -76,8 +76,8 @@ export default definePlugin({
|
||||||
find: "#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}",
|
find: "#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}",
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /(\i)\.Tooltip,{(text:.{0,30}#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}\))/,
|
match: /\i\.\i,{(text:.{0,30}#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}\))/,
|
||||||
replace: "$self.TooltipWrapper,{message:arguments[0].message,$2"
|
replace: "$self.TooltipWrapper,{message:arguments[0].message,$1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import "./spotifyStyles.css";
|
import "./spotifyStyles.css";
|
||||||
|
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { Flex } from "@components/Flex";
|
import { Flex } from "@components/Flex";
|
||||||
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||||
import { debounce } from "@shared/debounce";
|
import { debounce } from "@shared/debounce";
|
||||||
|
@ -28,7 +29,7 @@ import { ContextMenuApi, FluxDispatcher, Forms, Menu, React, useEffect, useState
|
||||||
|
|
||||||
import { SpotifyStore, Track } from "./SpotifyStore";
|
import { SpotifyStore, Track } from "./SpotifyStore";
|
||||||
|
|
||||||
const cl = (className: string) => `vc-spotify-${className}`;
|
const cl = classNameFactory("vc-spotify-");
|
||||||
|
|
||||||
function msToHuman(ms: number) {
|
function msToHuman(ms: number) {
|
||||||
const minutes = ms / 1000 / 60;
|
const minutes = ms / 1000 / 60;
|
||||||
|
@ -40,7 +41,7 @@ function msToHuman(ms: number) {
|
||||||
function Svg(path: string, label: string) {
|
function Svg(path: string, label: string) {
|
||||||
return () => (
|
return () => (
|
||||||
<svg
|
<svg
|
||||||
className={classes(cl("button-icon"), cl(label))}
|
className={cl("button-icon", label)}
|
||||||
height="24"
|
height="24"
|
||||||
width="24"
|
width="24"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
@ -126,7 +127,7 @@ function Controls() {
|
||||||
return (
|
return (
|
||||||
<Flex className={cl("button-row")} style={{ gap: 0 }}>
|
<Flex className={cl("button-row")} style={{ gap: 0 }}>
|
||||||
<Button
|
<Button
|
||||||
className={classes(cl("button"), cl(shuffle ? "shuffle-on" : "shuffle-off"))}
|
className={classes(cl("button"), cl("shuffle"), cl(shuffle ? "shuffle-on" : "shuffle-off"))}
|
||||||
onClick={() => SpotifyStore.setShuffle(!shuffle)}
|
onClick={() => SpotifyStore.setShuffle(!shuffle)}
|
||||||
>
|
>
|
||||||
<Shuffle />
|
<Shuffle />
|
||||||
|
@ -143,7 +144,7 @@ function Controls() {
|
||||||
<SkipNext />
|
<SkipNext />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
className={classes(cl("button"), cl(repeatClassName))}
|
className={classes(cl("button"), cl("repeat"), cl(repeatClassName))}
|
||||||
onClick={() => SpotifyStore.setRepeat(nextRepeat)}
|
onClick={() => SpotifyStore.setRepeat(nextRepeat)}
|
||||||
style={{ position: "relative" }}
|
style={{ position: "relative" }}
|
||||||
>
|
>
|
||||||
|
@ -285,7 +286,8 @@ function Info({ track }: { track: Track; }) {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (coverExpanded && img) return (
|
if (coverExpanded && img)
|
||||||
|
return (
|
||||||
<div id={cl("album-expanded-wrapper")}>
|
<div id={cl("album-expanded-wrapper")}>
|
||||||
{i}
|
{i}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -30,22 +30,17 @@
|
||||||
background-color: var(--background-modifier-selected);
|
background-color: var(--background-modifier-selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-spotify-button svg {
|
.vc-spotify-button-icon {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[class*="vc-spotify-shuffle"] > svg,
|
.vc-spotify-shuffle .vc-spotify-button-icon,
|
||||||
[class*="vc-spotify-repeat"] > svg {
|
.vc-spotify-repeat .vc-spotify-button-icon {
|
||||||
width: 22px;
|
width: 22px;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-spotify-button svg path {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* .vc-spotify-button:hover {
|
/* .vc-spotify-button:hover {
|
||||||
filter: brightness(1.3);
|
filter: brightness(1.3);
|
||||||
} */
|
} */
|
||||||
|
@ -87,12 +82,19 @@
|
||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#vc-spotify-info-wrapper img {
|
#vc-spotify-album-image {
|
||||||
height: 90%;
|
height: 90%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: filter 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
#vc-spotify-album-expanded-wrapper img {
|
#vc-spotify-album-image:hover {
|
||||||
|
filter: brightness(1.2);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#vc-spotify-album-expanded-wrapper #vc-spotify-album-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
@ -137,16 +139,6 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
#vc-spotify-album-image {
|
|
||||||
border-radius: 3px;
|
|
||||||
transition: filter 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#vc-spotify-album-image:hover {
|
|
||||||
filter: brightness(1.2);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#vc-spotify-progress-bar {
|
#vc-spotify-progress-bar {
|
||||||
position: relative;
|
position: relative;
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
|
|
|
@ -45,7 +45,6 @@ const makeEmptyRuleArray = () => [makeEmptyRule()];
|
||||||
const settings = definePluginSettings({
|
const settings = definePluginSettings({
|
||||||
replace: {
|
replace: {
|
||||||
type: OptionType.COMPONENT,
|
type: OptionType.COMPONENT,
|
||||||
description: "",
|
|
||||||
component: () => {
|
component: () => {
|
||||||
const { stringRules, regexRules } = settings.use(["stringRules", "regexRules"]);
|
const { stringRules, regexRules } = settings.use(["stringRules", "regexRules"]);
|
||||||
|
|
||||||
|
@ -245,7 +244,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
// TODO: Remove DataStore rules migrations once enough time has passed
|
// TODO(OptionType.CUSTOM Related): Remove DataStore rules migrations once enough time has passed
|
||||||
const oldStringRules = await DataStore.get<Rule[]>(STRING_RULES_KEY);
|
const oldStringRules = await DataStore.get<Rule[]>(STRING_RULES_KEY);
|
||||||
if (oldStringRules != null) {
|
if (oldStringRules != null) {
|
||||||
settings.store.stringRules = oldStringRules;
|
settings.store.stringRules = oldStringRules;
|
||||||
|
|
|
@ -76,7 +76,7 @@ export function TranslateModal({ rootProps }: { rootProps: ModalProps; }) {
|
||||||
return (
|
return (
|
||||||
<ModalRoot {...rootProps}>
|
<ModalRoot {...rootProps}>
|
||||||
<ModalHeader className={cl("modal-header")}>
|
<ModalHeader className={cl("modal-header")}>
|
||||||
<Forms.FormTitle tag="h2">
|
<Forms.FormTitle tag="h2" className={cl("modal-title")}>
|
||||||
Translate
|
Translate
|
||||||
</Forms.FormTitle>
|
</Forms.FormTitle>
|
||||||
<ModalCloseButton onClick={rootProps.onClose} />
|
<ModalCloseButton onClick={rootProps.onClose} />
|
||||||
|
|
|
@ -55,7 +55,7 @@ export function TranslationAccessory({ message }: { message: Message; }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={cl("accessory")}>
|
<span className={cl("accessory")}>
|
||||||
<TranslateIcon width={16} height={16} />
|
<TranslateIcon width={16} height={16} className={cl("accessory-icon")} />
|
||||||
{Parser.parse(translation.text)}
|
{Parser.parse(translation.text)}
|
||||||
{" "}
|
{" "}
|
||||||
(translated from {translation.sourceLanguage} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
(translated from {translation.sourceLanguage} - <Dismiss onDismiss={() => setTranslation(undefined)} />)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
place-content: center space-between;
|
place-content: center space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-trans-modal-header h1 {
|
.vc-trans-modal-title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-trans-accessory svg {
|
.vc-trans-accessory-icon {
|
||||||
margin-right: 0.25em;
|
margin-right: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,12 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { getIntlMessage } from "@utils/discord";
|
import { getIntlMessage } from "@utils/discord";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
|
import { findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||||
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
import { buildSeveralUsers } from "../typingTweaks";
|
import { buildSeveralUsers } from "../typingTweaks";
|
||||||
|
|
||||||
const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots");
|
const ThreeDots = findComponentByCodeLazy(".dots,", "dotRadius:");
|
||||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
|
|
||||||
const TypingStore = findStoreLazy("TypingStore");
|
const TypingStore = findStoreLazy("TypingStore");
|
||||||
|
@ -100,6 +100,13 @@ function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: s
|
||||||
{props => (
|
{props => (
|
||||||
<div className="vc-typing-indicator" {...props}>
|
<div className="vc-typing-indicator" {...props}>
|
||||||
{((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && (
|
{((settings.store.indicatorMode & IndicatorMode.Avatars) === IndicatorMode.Avatars) && (
|
||||||
|
<div
|
||||||
|
onClick={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
onKeyPress={e => e.stopPropagation()}
|
||||||
|
>
|
||||||
<UserSummaryItem
|
<UserSummaryItem
|
||||||
users={typingUsersArray.map(id => UserStore.getUser(id))}
|
users={typingUsersArray.map(id => UserStore.getUser(id))}
|
||||||
guildId={guildId}
|
guildId={guildId}
|
||||||
|
@ -110,6 +117,7 @@ function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: s
|
||||||
size={16}
|
size={16}
|
||||||
className="vc-typing-indicator-avatars"
|
className="vc-typing-indicator-avatars"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && (
|
{((settings.store.indicatorMode & IndicatorMode.Dots) === IndicatorMode.Dots) && (
|
||||||
<div className="vc-typing-indicator-dots">
|
<div className="vc-typing-indicator-dots">
|
||||||
|
|
|
@ -21,12 +21,18 @@ import { ImageInvisible, ImageVisible } from "@components/Icons";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { Constants, Menu, PermissionsBits, PermissionStore, RestAPI, UserStore } from "@webpack/common";
|
import { Constants, Menu, PermissionsBits, PermissionStore, RestAPI, UserStore } from "@webpack/common";
|
||||||
|
import { MessageSnapshot } from "@webpack/types";
|
||||||
|
|
||||||
|
|
||||||
const EMBED_SUPPRESSED = 1 << 2;
|
const EMBED_SUPPRESSED = 1 << 2;
|
||||||
|
|
||||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { channel, message: { author, embeds, flags, id: messageId } }) => {
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { channel, message: { author, messageSnapshots, embeds, flags, id: messageId } }) => {
|
||||||
const isEmbedSuppressed = (flags & EMBED_SUPPRESSED) !== 0;
|
const isEmbedSuppressed = (flags & EMBED_SUPPRESSED) !== 0;
|
||||||
if (!isEmbedSuppressed && !embeds.length) return;
|
const hasEmbedsInSnapshots = messageSnapshots.some(
|
||||||
|
(snapshot: MessageSnapshot) => snapshot?.message.embeds.length
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isEmbedSuppressed && !embeds.length && !hasEmbedsInSnapshots) return;
|
||||||
|
|
||||||
const hasEmbedPerms = channel.isPrivate() || !!(PermissionStore.getChannelPermissions({ id: channel.id }) & PermissionsBits.EMBED_LINKS);
|
const hasEmbedPerms = channel.isPrivate() || !!(PermissionStore.getChannelPermissions({ id: channel.id }) & PermissionsBits.EMBED_LINKS);
|
||||||
if (author.id === UserStore.getCurrentUser().id && !hasEmbedPerms) return;
|
if (author.id === UserStore.getCurrentUser().id && !hasEmbedPerms) return;
|
||||||
|
|
|
@ -22,7 +22,7 @@ const VoiceStateStore = findStoreLazy("VoiceStateStore");
|
||||||
|
|
||||||
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
|
||||||
const Avatar = findComponentByCodeLazy(".status)/2):0");
|
const Avatar = findComponentByCodeLazy(".status)/2):0");
|
||||||
const GroupDMAvatars = findComponentByCodeLazy(".AvatarSizeSpecs[", "getAvatarURL");
|
const GroupDMAvatars = findComponentByCodeLazy("frontSrc:", "getAvatarURL");
|
||||||
|
|
||||||
const ActionButtonClasses = findByPropsLazy("actionButton", "highlight");
|
const ActionButtonClasses = findByPropsLazy("actionButton", "highlight");
|
||||||
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
:is([class*="userProfile"], [class*="userPopout"]) [class*="bannerPremium"] {
|
|
||||||
background: center / cover no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
[class*="NonPremium"]:has([class*="bannerPremium"]) [class*="avatarPositionNormal"],
|
|
||||||
[class*="PremiumWithoutBanner"]:has([class*="bannerPremium"]) [class*="avatarPositionPremiumNoBanner"] {
|
|
||||||
top: 76px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[style*="background-image"] [class*="background_"] {
|
|
||||||
background-color: transparent !important;
|
|
||||||
}
|
|
|
@ -21,8 +21,6 @@ import { Link } from "@components/Link";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
|
|
||||||
import style from "./index.css?managed";
|
|
||||||
|
|
||||||
const API_URL = "https://usrbg.is-hardly.online/users";
|
const API_URL = "https://usrbg.is-hardly.online/users";
|
||||||
|
|
||||||
interface UsrbgApiReturn {
|
interface UsrbgApiReturn {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
.vc-toolbox-btn,
|
.vc-toolbox-btn,
|
||||||
.vc-toolbox-btn>svg {
|
.vc-toolbox-icon {
|
||||||
-webkit-app-region: no-drag;
|
-webkit-app-region: no-drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-toolbox-btn>svg {
|
.vc-toolbox-icon {
|
||||||
color: var(--interactive-normal);
|
color: var(--interactive-normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-toolbox-btn[class*="selected"]>svg {
|
.vc-toolbox-btn[class*="selected"] .vc-toolbox-icon {
|
||||||
color: var(--interactive-active);
|
color: var(--interactive-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vc-toolbox-btn:hover>svg {
|
.vc-toolbox-btn:hover .vc-toolbox-icon {
|
||||||
color: var(--interactive-hover);
|
color: var(--interactive-hover);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,11 @@ import { Settings, useSettings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findExportedComponentLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { Menu, Popout, useState } from "@webpack/common";
|
import { Menu, Popout, useState } from "@webpack/common";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider");
|
const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"');
|
||||||
|
|
||||||
function VencordPopout(onClose: () => void) {
|
function VencordPopout(onClose: () => void) {
|
||||||
const { useQuickCss } = useSettings(["useQuickCss"]);
|
const { useQuickCss } = useSettings(["useQuickCss"]);
|
||||||
|
@ -88,7 +88,7 @@ function VencordPopout(onClose: () => void) {
|
||||||
|
|
||||||
function VencordPopoutIcon(isShown: boolean) {
|
function VencordPopoutIcon(isShown: boolean) {
|
||||||
return (
|
return (
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" width={24} height={24}>
|
<svg viewBox="0 0 27 27" width={24} height={24} className="vc-toolbox-icon">
|
||||||
<path fill="currentColor" d={isShown ? "M9 0h1v1h1v2h1v2h3V3h1V1h1V0h1v2h1v2h1v7h-1v-1h-3V9h1V6h-1v4h-3v1h1v-1h2v1h3v1h-1v1h-3v2h1v1h1v1h1v3h-1v4h-2v-1h-1v-4h-1v4h-1v1h-2v-4H9v-3h1v-1h1v-1h1v-2H9v-1H8v-1h3V6h-1v3h1v1H8v1H7V4h1V2h1M5 19h2v1h1v1h1v3H4v-1h2v-1H4v-2h1m15-1h2v1h1v2h-2v1h2v1h-5v-3h1v-1h1m4 3h4v1h-4" : "M0 0h7v1H6v1H5v1H4v1H3v1H2v1h5v1H0V6h1V5h1V4h1V3h1V2h1V1H0m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5V7h1V6h1V5h1V4h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1M8 14h2v1H9v4h1v2h1v1h1v1h1v1h4v1h-6v-1H5v-1H4v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1"} />
|
<path fill="currentColor" d={isShown ? "M9 0h1v1h1v2h1v2h3V3h1V1h1V0h1v2h1v2h1v7h-1v-1h-3V9h1V6h-1v4h-3v1h1v-1h2v1h3v1h-1v1h-3v2h1v1h1v1h1v3h-1v4h-2v-1h-1v-4h-1v4h-1v1h-2v-4H9v-3h1v-1h1v-1h1v-2H9v-1H8v-1h3V6h-1v3h1v1H8v1H7V4h1V2h1M5 19h2v1h1v1h1v3H4v-1h2v-1H4v-2h1m15-1h2v1h1v2h-2v1h2v1h-5v-3h1v-1h1m4 3h4v1h-4" : "M0 0h7v1H6v1H5v1H4v1H3v1H2v1h5v1H0V6h1V5h1V4h1V3h1V2h1V1H0m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5V7h1V6h1V5h1V4h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1M8 14h2v1H9v4h1v2h1v1h1v1h1v1h4v1h-6v-1H5v-1H4v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1"} />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue