mirror of
https://github.com/Vendicated/Vencord.git
synced 2025-01-09 17:36:23 +00:00
Merge branch 'dev' into fix/betterFolders
This commit is contained in:
commit
ad4e2e6594
87 changed files with 2270 additions and 1721 deletions
|
@ -4,10 +4,9 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
import stylistic from "@stylistic/eslint-plugin";
|
||||
import pathAlias from "eslint-plugin-path-alias";
|
||||
import react from "eslint-plugin-react";
|
||||
import header from "eslint-plugin-simple-header";
|
||||
import simpleImportSort from "eslint-plugin-simple-import-sort";
|
||||
import unusedImports from "eslint-plugin-unused-imports";
|
||||
|
@ -15,6 +14,22 @@ import tseslint from "typescript-eslint";
|
|||
|
||||
export default tseslint.config(
|
||||
{ ignores: ["dist", "browser", "packages/vencord-types"] },
|
||||
{
|
||||
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}", "eslint.config.mjs"],
|
||||
settings: {
|
||||
react: {
|
||||
version: "18"
|
||||
}
|
||||
},
|
||||
...react.configs.flat.recommended,
|
||||
rules: {
|
||||
...react.configs.flat.recommended.rules,
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/no-unescaped-entities": "off",
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ["src/**/*.{tsx,ts,mts,mjs,js,jsx}", "eslint.config.mjs"],
|
||||
plugins: {
|
||||
|
@ -23,7 +38,7 @@ export default tseslint.config(
|
|||
"@typescript-eslint": tseslint.plugin,
|
||||
"simple-import-sort": simpleImportSort,
|
||||
"unused-imports": unusedImports,
|
||||
"path-alias": pathAlias,
|
||||
"path-alias": pathAlias
|
||||
},
|
||||
settings: {
|
||||
"import/resolver": {
|
||||
|
|
53
package.json
53
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.10.8",
|
||||
"version": "1.10.9",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -41,48 +41,49 @@
|
|||
"@vap/shiki": "0.10.5",
|
||||
"fflate": "^0.8.2",
|
||||
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"nanoid": "^5.0.9",
|
||||
"virtual-merge": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@stylistic/eslint-plugin": "^2.6.1",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"@types/node": "^22.0.3",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@stylistic/eslint-plugin": "^2.12.1",
|
||||
"@types/chrome": "^0.0.287",
|
||||
"@types/diff": "^6.0.0",
|
||||
"@types/lodash": "^4.17.14",
|
||||
"@types/node": "^22.10.5",
|
||||
"@types/react": "^19.0.2",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@types/yazl": "^2.4.5",
|
||||
"diff": "^5.2.0",
|
||||
"diff": "^7.0.0",
|
||||
"discord-types": "^1.3.26",
|
||||
"esbuild": "^0.15.18",
|
||||
"eslint": "^9.8.0",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-path-alias": "2.1.0",
|
||||
"eslint-plugin-simple-header": "^1.1.1",
|
||||
"eslint-plugin-react": "^7.37.3",
|
||||
"eslint-plugin-simple-header": "^1.2.1",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-unused-imports": "^4.0.1",
|
||||
"highlight.js": "10.7.3",
|
||||
"eslint-plugin-unused-imports": "^4.1.4",
|
||||
"highlight.js": "11.7.0",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
"moment": "^2.30.1",
|
||||
"puppeteer-core": "^22.15.0",
|
||||
"moment": "^2.22.2",
|
||||
"puppeteer-core": "^23.11.1",
|
||||
"standalone-electron-types": "^1.0.0",
|
||||
"stylelint": "^16.8.1",
|
||||
"stylelint": "^16.12.0",
|
||||
"stylelint-config-standard": "^36.0.1",
|
||||
"ts-patch": "^3.2.1",
|
||||
"ts-pattern": "^5.3.1",
|
||||
"tsx": "^4.16.5",
|
||||
"type-fest": "^4.23.0",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.0.0",
|
||||
"typescript-transform-paths": "^3.4.7",
|
||||
"ts-patch": "^3.3.0",
|
||||
"ts-pattern": "^5.6.0",
|
||||
"tsx": "^4.19.2",
|
||||
"type-fest": "^4.31.0",
|
||||
"typescript": "^5.7.2",
|
||||
"typescript-eslint": "^8.19.0",
|
||||
"typescript-transform-paths": "^3.5.3",
|
||||
"zip-local": "^0.3.5"
|
||||
},
|
||||
"packageManager": "pnpm@9.1.0",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"eslint@9.8.0": "patches/eslint@9.8.0.patch",
|
||||
"eslint@9.17.0": "patches/eslint@9.17.0.patch",
|
||||
"eslint-plugin-path-alias@2.1.0": "patches/eslint-plugin-path-alias@2.1.0.patch"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
|
|
3259
pnpm-lock.yaml
3259
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,7 @@ import { Logger } from "@utils/Logger";
|
|||
import { waitFor } from "@webpack";
|
||||
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
|
||||
import { Channel } from "discord-types/general";
|
||||
import { HTMLProps, MouseEventHandler, ReactNode } from "react";
|
||||
import { HTMLProps, JSX, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>;
|
||||
waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m);
|
||||
|
|
|
@ -24,13 +24,13 @@ import type { ReactElement } from "react";
|
|||
* @param children The rendered context menu elements
|
||||
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
||||
*/
|
||||
export type NavContextMenuPatchCallback = (children: Array<ReactElement | null>, ...args: Array<any>) => void;
|
||||
export type NavContextMenuPatchCallback = (children: Array<ReactElement<any> | null>, ...args: Array<any>) => void;
|
||||
/**
|
||||
* @param navId The navId of the context menu being patched
|
||||
* @param children The rendered context menu elements
|
||||
* @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example
|
||||
*/
|
||||
export type GlobalContextMenuPatchCallback = (navId: string, children: Array<ReactElement | null>, ...args: Array<any>) => void;
|
||||
export type GlobalContextMenuPatchCallback = (navId: string, children: Array<ReactElement<any> | null>, ...args: Array<any>) => void;
|
||||
|
||||
const ContextMenuLogger = new Logger("ContextMenu");
|
||||
|
||||
|
@ -70,7 +70,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
|
|||
* @returns Whether the patch was successfully removed from the context menu(s)
|
||||
*/
|
||||
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
|
||||
const navIds = Array.isArray(navId) ? navId : [navId as string];
|
||||
const navIds: string[] = Array.isArray(navId) ? navId : [navId];
|
||||
|
||||
const results = navIds.map(id => navPatches.get(id)?.delete(patch) ?? false);
|
||||
|
||||
|
@ -92,7 +92,7 @@ export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallba
|
|||
* @param children The context menu children
|
||||
* @param matchSubstring Whether to check if the id is a substring of the child id
|
||||
*/
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement | null | undefined>, matchSubstring = false): Array<ReactElement | null | undefined> | null {
|
||||
export function findGroupChildrenByChildId(id: string | string[], children: Array<ReactElement<any> | null | undefined>, matchSubstring = false): Array<ReactElement<any> | null | undefined> | null {
|
||||
for (const child of children) {
|
||||
if (child == null) continue;
|
||||
|
||||
|
@ -124,7 +124,7 @@ export function findGroupChildrenByChildId(id: string | string[], children: Arra
|
|||
interface ContextMenuProps {
|
||||
contextMenuApiArguments?: Array<any>;
|
||||
navId: string;
|
||||
children: Array<ReactElement | null>;
|
||||
children: Array<ReactElement<any> | null>;
|
||||
"aria-label": string;
|
||||
onSelect: (() => void) | undefined;
|
||||
onClose: (callback: (...args: Array<any>) => any) => void;
|
||||
|
@ -162,7 +162,7 @@ export function _usePatchContextMenu(props: ContextMenuProps) {
|
|||
return props;
|
||||
}
|
||||
|
||||
function cloneMenuChildren(obj: ReactElement | Array<ReactElement | null> | null) {
|
||||
function cloneMenuChildren(obj: ReactElement<any> | Array<ReactElement<any> | null> | null) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(cloneMenuChildren);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Channel, User } from "discord-types/general/index.js";
|
||||
import { JSX } from "react";
|
||||
|
||||
interface DecoratorProps {
|
||||
activities: any[];
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { JSX } from "react";
|
||||
|
||||
export type AccessoryCallback = (props: Record<string, any>) => JSX.Element | null | Array<JSX.Element | null>;
|
||||
export type Accessory = {
|
||||
callback: AccessoryCallback;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Channel, Message } from "discord-types/general/index.js";
|
||||
import { JSX } from "react";
|
||||
|
||||
interface DecorationProps {
|
||||
author: {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { JSX } from "react";
|
||||
|
||||
const logger = new Logger("ServerListAPI");
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Logger } from "@utils/Logger";
|
|||
import { mergeDefaults } from "@utils/mergeDefaults";
|
||||
import { putCloudSettings } from "@utils/settingsSync";
|
||||
import { DefinedSettings, OptionType, SettingsChecks, SettingsDefinition } from "@utils/types";
|
||||
import { React } from "@webpack/common";
|
||||
import { React, useEffect } from "@webpack/common";
|
||||
|
||||
import plugins from "~plugins";
|
||||
|
||||
|
@ -192,7 +192,7 @@ export const Settings = SettingsStore.store;
|
|||
export function useSettings(paths?: UseSettings<Settings>[]) {
|
||||
const [, forceUpdate] = React.useReducer(() => ({}), {});
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (paths) {
|
||||
paths.forEach(p => SettingsStore.addChangeListener(p, forceUpdate));
|
||||
return () => paths.forEach(p => SettingsStore.removeChangeListener(p, forceUpdate));
|
||||
|
@ -200,7 +200,7 @@ export function useSettings(paths?: UseSettings<Settings>[]) {
|
|||
SettingsStore.addGlobalChangeListener(forceUpdate);
|
||||
return () => SettingsStore.removeGlobalChangeListener(forceUpdate);
|
||||
}
|
||||
}, []);
|
||||
}, [paths]);
|
||||
|
||||
return SettingsStore.store;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export function Badge({ text, color }): JSX.Element {
|
||||
export function Badge({ text, color }) {
|
||||
return (
|
||||
<div className="vc-plugins-badge" style={{
|
||||
backgroundColor: color,
|
||||
|
|
|
@ -27,7 +27,7 @@ interface Props<T = any> {
|
|||
/** Render nothing if an error occurs */
|
||||
noop?: boolean;
|
||||
/** Fallback component to render if an error occurs */
|
||||
fallback?: React.ComponentType<React.PropsWithChildren<{ error: any; message: string; stack: string; }>>;
|
||||
fallback?: React.ComponentType<React.PropsWithChildren<{ error: any; message: string; stack: string; wrappedProps: T; }>>;
|
||||
/** called when an error occurs. The props property is only available if using .wrap */
|
||||
onError?(data: { error: Error, errorInfo: React.ErrorInfo, props: T; }): void;
|
||||
/** Custom error message */
|
||||
|
@ -80,10 +80,14 @@ const ErrorBoundary = LazyComponent(() => {
|
|||
if (this.props.noop) return null;
|
||||
|
||||
if (this.props.fallback)
|
||||
return <this.props.fallback
|
||||
children={this.props.children}
|
||||
return (
|
||||
<this.props.fallback
|
||||
wrappedProps={this.props.wrappedProps}
|
||||
{...this.state}
|
||||
/>;
|
||||
>
|
||||
{this.props.children}
|
||||
</this.props.fallback>
|
||||
);
|
||||
|
||||
const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console.";
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { CSSProperties } from "react";
|
||||
import { CSSProperties, JSX } from "react";
|
||||
|
||||
interface Props {
|
||||
columns: number;
|
||||
|
|
|
@ -27,7 +27,7 @@ export function Heart() {
|
|||
>
|
||||
<path
|
||||
fill="#db61a2"
|
||||
fill-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"
|
||||
/>
|
||||
</svg>
|
||||
|
|
|
@ -20,7 +20,7 @@ import "./iconStyles.css";
|
|||
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import { classes } from "@utils/misc";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import type { JSX, PropsWithChildren } from "react";
|
||||
|
||||
interface BaseIconProps extends IconProps {
|
||||
viewBox: string;
|
||||
|
@ -55,7 +55,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
|
|||
className={classes(className, "vc-link-icon")}
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" />
|
||||
<rect width={width} height={height} />
|
||||
</g>
|
||||
|
@ -122,8 +122,8 @@ export function InfoIcon(props: IconProps) {
|
|||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
d="M23 12a11 11 0 1 1-22 0 11 11 0 0 1 22 0Zm-9.5-4.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm-.77 3.96a1 1 0 1 0-1.96-.42l-1.04 4.86a2.77 2.77 0 0 0 4.31 2.83l.24-.17a1 1 0 1 0-1.16-1.62l-.24.17a.77.77 0 0 1-1.2-.79l1.05-4.86Z" clip-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
d="M23 12a11 11 0 1 1-22 0 11 11 0 0 1 22 0Zm-9.5-4.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm-.77 3.96a1 1 0 1 0-1.96-.42l-1.04 4.86a2.77 2.77 0 0 0 4.31 2.83l.24-.17a1 1 0 1 0-1.16-1.62l-.24.17a.77.77 0 0 1-1.2-.79l1.05-4.86Z" clipRule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -212,9 +212,9 @@ export function CogWheel(props: IconProps) {
|
|||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
d="M10.56 1.1c-.46.05-.7.53-.64.98.18 1.16-.19 2.2-.98 2.53-.8.33-1.79-.15-2.49-1.1-.27-.36-.78-.52-1.14-.24-.77.59-1.45 1.27-2.04 2.04-.28.36-.12.87.24 1.14.96.7 1.43 1.7 1.1 2.49-.33.8-1.37 1.16-2.53.98-.45-.07-.93.18-.99.64a11.1 11.1 0 0 0 0 2.88c.06.46.54.7.99.64 1.16-.18 2.2.19 2.53.98.33.8-.14 1.79-1.1 2.49-.36.27-.52.78-.24 1.14.59.77 1.27 1.45 2.04 2.04.36.28.87.12 1.14-.24.7-.95 1.7-1.43 2.49-1.1.8.33 1.16 1.37.98 2.53-.07.45.18.93.64.99a11.1 11.1 0 0 0 2.88 0c.46-.06.7-.54.64-.99-.18-1.16.19-2.2.98-2.53.8-.33 1.79.14 2.49 1.1.27.36.78.52 1.14.24.77-.59 1.45-1.27 2.04-2.04.28-.36.12-.87-.24-1.14-.96-.7-1.43-1.7-1.1-2.49.33-.8 1.37-1.16 2.53-.98.45.07.93-.18.99-.64a11.1 11.1 0 0 0 0-2.88c-.06-.46-.54-.7-.99-.64-1.16.18-2.2-.19-2.53-.98-.33-.8.14-1.79 1.1-2.49.36-.27.52-.78.24-1.14a11.07 11.07 0 0 0-2.04-2.04c-.36-.28-.87-.12-1.14.24-.7.96-1.7 1.43-2.49 1.1-.8-.33-1.16-1.37-.98-2.53.07-.45-.18-.93-.64-.99a11.1 11.1 0 0 0-2.88 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"
|
||||
clip-rule="evenodd"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</Icon>
|
||||
);
|
||||
|
@ -262,7 +262,7 @@ export function PlusIcon(props: IconProps) {
|
|||
viewBox="0 0 18 18"
|
||||
>
|
||||
<polygon
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
fill="currentColor"
|
||||
points="15 10 10 10 10 15 8 15 8 10 3 10 3 8 8 8 8 3 10 3 10 8 15 8"
|
||||
/>
|
||||
|
|
|
@ -44,7 +44,7 @@ function ContributorModal({ user }: { user: User; }) {
|
|||
useEffect(() => {
|
||||
if (!profile && !user.bot && user.id)
|
||||
fetchUserProfile(user.id);
|
||||
}, [user.id]);
|
||||
}, [user.id, user.bot, profile]);
|
||||
|
||||
const githubName = profile?.connectedAccounts?.find(a => a.type === "github")?.name;
|
||||
const website = profile?.connectedAccounts?.find(a => a.type === "domain")?.name;
|
||||
|
|
|
@ -109,7 +109,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
setAuthors(a => [...a, author]);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
}, [plugin.authors]);
|
||||
|
||||
async function saveAndClose() {
|
||||
if (!plugin.options) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import { useAwaiter } from "@utils/react";
|
|||
import { Plugin } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import Plugins, { ExcludedPlugins } from "~plugins";
|
||||
|
||||
|
@ -387,7 +388,7 @@ function makeDependencyList(deps: string[]) {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<Forms.FormText>This plugin is required by:</Forms.FormText>
|
||||
{deps.map((dep: string) => <Forms.FormText className={cl("dep-text")}>{dep}</Forms.FormText>)}
|
||||
{deps.map((dep: string) => <Forms.FormText key={dep} className={cl("dep-text")}>{dep}</Forms.FormText>)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -111,9 +111,9 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
|
|||
}
|
||||
|
||||
function renderDiff() {
|
||||
return diff?.map(p => {
|
||||
return diff?.map((p, idx) => {
|
||||
const color = p.added ? "lime" : p.removed ? "red" : "grey";
|
||||
return <div style={{ color, userSelect: "text", wordBreak: "break-all", lineBreak: "anywhere" }}>{p.value}</div>;
|
||||
return <div key={idx} style={{ color, userSelect: "text", wordBreak: "break-all", lineBreak: "anywhere" }}>{p.value}</div>;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
|
|||
title: "Oops!",
|
||||
body: (
|
||||
<ErrorCard>
|
||||
{err.split("\n").map(line => <div>{Parser.parse(line)}</div>)}
|
||||
{err.split("\n").map((line, idx) => <div key={idx}>{Parser.parse(line)}</div>)}
|
||||
</ErrorCard>
|
||||
)
|
||||
});
|
||||
|
@ -87,7 +87,7 @@ function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof
|
|||
return (
|
||||
<Card style={{ padding: "0 0.5em" }}>
|
||||
{updates.map(({ hash, author, message }) => (
|
||||
<div style={{
|
||||
<div key={hash} style={{
|
||||
marginTop: "0.5em",
|
||||
marginBottom: "0.5em"
|
||||
}}>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { onceDefined } from "@shared/onceDefined";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu, nativeTheme } from "electron";
|
||||
import electron, { app, BrowserWindowConstructorOptions, Menu } from "electron";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
import { initIpc } from "./ipcMain";
|
||||
|
@ -100,19 +100,6 @@ if (!IS_VANILLA) {
|
|||
|
||||
super(options);
|
||||
initIpc(this);
|
||||
|
||||
// Workaround for https://github.com/electron/electron/issues/43367. Vesktop also has its own workaround
|
||||
// @TODO: Remove this when the issue is fixed
|
||||
if (IS_DISCORD_DESKTOP) {
|
||||
this.webContents.on("devtools-opened", () => {
|
||||
if (!nativeTheme.shouldUseDarkColors) return;
|
||||
|
||||
nativeTheme.themeSource = "light";
|
||||
setTimeout(() => {
|
||||
nativeTheme.themeSource = "dark";
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
} else super(options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,13 +71,16 @@ export async function installExt(id: string) {
|
|||
// React Devtools v4.25
|
||||
// v4.27 is broken in Electron, see https://github.com/facebook/react/issues/25843
|
||||
// Unfortunately, Google does not serve old versions, so this is the only way
|
||||
// This zip file is pinned to long commit hash so it cannot be changed remotely
|
||||
? "https://raw.githubusercontent.com/Vendicated/random-files/f6f550e4c58ac5f2012095a130406c2ab25b984d/fmkadmapgofadopljbjfkapdkoienihi.zip"
|
||||
: `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${id}%26uc&prodversion=32`;
|
||||
: `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${id}%26uc&prodversion=${process.versions.chrome}`;
|
||||
|
||||
const buf = await get(url, {
|
||||
headers: {
|
||||
"User-Agent": "Vencord (https://github.com/Vendicated/Vencord)"
|
||||
"User-Agent": `Electron ${process.versions.electron} ~ Vencord (https://github.com/Vendicated/Vencord)`
|
||||
}
|
||||
});
|
||||
|
||||
await extract(crxToZip(buf), extDir).catch(console.error);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,14 @@ export default definePlugin({
|
|||
replace: "$&return;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".BetterDiscord||null!=",
|
||||
replacement: {
|
||||
// Make hasClientMods return false
|
||||
match: /(?=let \i=window;)/,
|
||||
replace: "return false;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import { makeCodeblock } from "@utils/text";
|
|||
import definePlugin from "@utils/types";
|
||||
import { checkForUpdates, isOutdated, update } from "@utils/updater";
|
||||
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Text, Toasts, UserStore } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import gitHash from "~git-hash";
|
||||
import plugins, { PluginMeta } from "~plugins";
|
||||
|
|
|
@ -85,7 +85,7 @@ export default definePlugin({
|
|||
replace: "$&onRequestClose:$self.onPopoutClose,"
|
||||
},
|
||||
{
|
||||
match: /(?<=.avatarWrapper,)/,
|
||||
match: /(?<=\.avatarWrapper,)/,
|
||||
replace: "ref:$self.accountPanelRef,onContextMenu:$self.openAccountPanelContextMenu,"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
|
||||
|
@ -26,7 +27,7 @@ interface RemoteData {
|
|||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
||||
|
||||
const APPLE_MUSIC_BUNDLE_REGEX = /<script type="module" crossorigin src="([a-zA-Z0-9.\-/]+)"><\/script>/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = /\w+="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)",\w+="x-apple-jingle-correlation-key"/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = canonicalizeMatch(/Promise.allSettled\(\i\)\}const \i="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)"/);
|
||||
|
||||
let cachedToken: string | undefined = undefined;
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ export default definePlugin({
|
|||
{
|
||||
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
|
||||
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
|
||||
match: /(?<=\.wrapper,children:\[)/,
|
||||
match: /(?<=\.isExpanded\),children:\[)/,
|
||||
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0]?.isBetterFolders,arguments[0]?.betterFoldersExpandedIds)&&"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -99,7 +99,7 @@ export const UnknownIcon = (props: React.PropsWithChildren<SVGProps<SVGSVGElemen
|
|||
fill="currentColor"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path fill-rule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215 0 1.344-.665 2.288-1.79 2.973-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712 1.03-.632 1.397-1.135 1.397-2.028 0-.979-.758-1.698-1.926-1.698-1.009 0-1.71.529-1.938 1.402-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09 0-.618-.473-1.092-1.095-1.092-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z" />
|
||||
<path fillRule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215 0 1.344-.665 2.288-1.79 2.973-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712 1.03-.632 1.397-1.135 1.397-2.028 0-.979-.758-1.698-1.926-1.698-1.009 0-1.71.529-1.938 1.402-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09 0-.618-.473-1.092-1.095-1.092-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ export default definePlugin({
|
|||
}
|
||||
return this;
|
||||
},
|
||||
map(render: (item: SettingsEntry) => ReactElement) {
|
||||
map(render: (item: SettingsEntry) => ReactElement<any>) {
|
||||
return items
|
||||
.filter(a => a.items.length > 0)
|
||||
.map(({ label, items }) => {
|
||||
|
@ -181,11 +181,14 @@ export default definePlugin({
|
|||
if (label) {
|
||||
return (
|
||||
<Menu.MenuItem
|
||||
key={label}
|
||||
id={label.replace(/\W/, "_")}
|
||||
label={label}
|
||||
children={children}
|
||||
action={children[0].props.action}
|
||||
/>);
|
||||
>
|
||||
{children}
|
||||
</Menu.MenuItem>
|
||||
);
|
||||
} else {
|
||||
return children;
|
||||
}
|
||||
|
|
|
@ -75,10 +75,11 @@ export default definePlugin({
|
|||
patches: [{
|
||||
find: "renderConnectionStatus(){",
|
||||
replacement: {
|
||||
match: /(?<=renderConnectionStatus\(\)\{.+\.channel,children:)\i/,
|
||||
match: /(?<=renderConnectionStatus\(\){.+\.channel,children:).+?}\):\i(?=}\))/,
|
||||
replace: "[$&, $self.renderTimer(this.props.channel.id)]"
|
||||
}
|
||||
}],
|
||||
|
||||
renderTimer(channelId: string) {
|
||||
return <ErrorBoundary noop>
|
||||
<this.Timer channelId={channelId} />
|
||||
|
|
|
@ -67,9 +67,16 @@ export default definePlugin({
|
|||
|
||||
patches: [
|
||||
{
|
||||
find: 'react-spring: The "interpolate" function',
|
||||
find: "https://github.com/highlightjs/highlight.js/issues/2277",
|
||||
replacement: {
|
||||
match: /,console.warn\('react-spring: The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
match: /(?<=&&\()console.log\(`Deprecated.+?`\),/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'The "interpolate" function is deprecated in v10 (use "to" instead)',
|
||||
replacement: {
|
||||
match: /,console.warn\(\i\+'The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
|
@ -126,7 +133,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "Slow dispatch on",
|
||||
replacement: {
|
||||
match: /\i\.totalTime>100&&\i\.verbose\("Slow dispatch on ".+?\)\);/,
|
||||
match: /\i\.totalTime>\i&&\i\.verbose\("Slow dispatch on ".+?\)\);/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Devs } from "@utils/constants";
|
|||
import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
|
||||
import { runtimeHashMessageKey } from "@utils/intlHash";
|
||||
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
|
||||
import { ModalAPI } from "@utils/modal";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
|
||||
import definePlugin, { PluginNative, StartAt } from "@utils/types";
|
||||
|
@ -132,7 +133,10 @@ function makeShortcuts() {
|
|||
});
|
||||
}
|
||||
|
||||
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
|
||||
const root = Common.ReactDOM.createRoot(doc.body.appendChild(document.createElement("div")));
|
||||
root.render(Common.React.createElement(component, props));
|
||||
|
||||
doc.addEventListener("close", () => root.unmount(), { once: true });
|
||||
},
|
||||
|
||||
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
|
||||
|
@ -144,6 +148,8 @@ function makeShortcuts() {
|
|||
me: { getter: () => Common.UserStore.getCurrentUser(), preload: false },
|
||||
meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false },
|
||||
messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false },
|
||||
openModal: { getter: () => ModalAPI.openModal },
|
||||
openModalLazy: { getter: () => ModalAPI.openModalLazy },
|
||||
|
||||
Stores: {
|
||||
getter: () => Object.fromEntries(
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { React } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { cl } from "../";
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { React } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { cl } from "../";
|
||||
import Grid, { GridProps } from "./Grid";
|
||||
|
|
|
@ -16,11 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
migratePluginSettings("DisableCallIdle", "DisableDMCallIdle");
|
||||
export default definePlugin({
|
||||
name: "DisableCallIdle",
|
||||
description: "Disables automatically getting kicked from a DM voice call after 3 minutes and being moved to an AFK voice channel.",
|
||||
|
|
|
@ -211,7 +211,7 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
|
|||
alignItems: "center"
|
||||
}}>
|
||||
{guilds.map(g => (
|
||||
<Tooltip text={g.name}>
|
||||
<Tooltip key={g.id} text={g.name}>
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<div
|
||||
onMouseLeave={onMouseLeave}
|
||||
|
|
|
@ -207,8 +207,8 @@ function makeBypassPatches(): Omit<Patch, "plugin"> {
|
|||
return {
|
||||
find: "canUseCustomStickersEverywhere:",
|
||||
replacement: mapping.map(({ func, predicate }) => ({
|
||||
match: new RegExp(String.raw`(?<=${func}:function\(\i(?:,\i)?\){)`),
|
||||
replace: "return true;",
|
||||
match: new RegExp(String.raw`(?<=${func}:)\i`),
|
||||
replace: "() => true",
|
||||
predicate
|
||||
}))
|
||||
};
|
||||
|
@ -297,8 +297,8 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Overwrite incoming connection settings proto with our local settings
|
||||
match: /CONNECTION_OPEN:function\((\i)\){/,
|
||||
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
match: /function (\i)\((\i)\){(?=.*CONNECTION_OPEN:\1)/,
|
||||
replace: (m, funcName, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
},
|
||||
{
|
||||
// Overwrite non local proto changes with our local settings
|
||||
|
@ -514,7 +514,7 @@ export default definePlugin({
|
|||
return array.filter(item => item != null);
|
||||
},
|
||||
|
||||
ensureChildrenIsArray(child: ReactElement) {
|
||||
ensureChildrenIsArray(child: ReactElement<any>) {
|
||||
if (!Array.isArray(child.props.children)) child.props.children = [child.props.children];
|
||||
},
|
||||
|
||||
|
@ -524,7 +524,7 @@ export default definePlugin({
|
|||
|
||||
let nextIndex = content.length;
|
||||
|
||||
const transformLinkChild = (child: ReactElement) => {
|
||||
const transformLinkChild = (child: ReactElement<any>) => {
|
||||
if (settings.store.transformEmojis) {
|
||||
const fakeNitroMatch = child.props.href.match(fakeNitroEmojiRegex);
|
||||
if (fakeNitroMatch) {
|
||||
|
@ -558,7 +558,7 @@ export default definePlugin({
|
|||
return child;
|
||||
};
|
||||
|
||||
const transformChild = (child: ReactElement) => {
|
||||
const transformChild = (child: ReactElement<any>) => {
|
||||
if (child?.props?.trusted != null) return transformLinkChild(child);
|
||||
if (child?.props?.children != null) {
|
||||
if (!Array.isArray(child.props.children)) {
|
||||
|
@ -574,7 +574,7 @@ export default definePlugin({
|
|||
return child;
|
||||
};
|
||||
|
||||
const modifyChild = (child: ReactElement) => {
|
||||
const modifyChild = (child: ReactElement<any>) => {
|
||||
const newChild = transformChild(child);
|
||||
|
||||
if (newChild?.type === "ul" || newChild?.type === "ol") {
|
||||
|
@ -601,7 +601,7 @@ export default definePlugin({
|
|||
return newChild;
|
||||
};
|
||||
|
||||
const modifyChildren = (children: Array<ReactElement>) => {
|
||||
const modifyChildren = (children: Array<ReactElement<any>>) => {
|
||||
for (const [index, child] of children.entries()) children[index] = modifyChild(child);
|
||||
|
||||
children = this.clearEmptyArrayItems(children);
|
||||
|
|
|
@ -29,6 +29,7 @@ import definePlugin, { OptionType } from "@utils/types";
|
|||
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { ReactElement } from "react";
|
||||
import virtualMerge from "virtual-merge";
|
||||
|
||||
interface UserProfile extends User {
|
||||
|
@ -87,7 +88,7 @@ const settings = definePluginSettings({
|
|||
|
||||
interface ColorPickerProps {
|
||||
color: number | null;
|
||||
label: React.ReactElement;
|
||||
label: ReactElement<any>;
|
||||
showEyeDropper?: boolean;
|
||||
suggestedColors?: string[];
|
||||
onChange(value: number | null): void;
|
||||
|
|
|
@ -57,7 +57,7 @@ export default definePlugin({
|
|||
{
|
||||
// https://regex101.com/r/x2mobQ/1
|
||||
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
||||
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/,
|
||||
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(Math\.max\(\i,\i(?:-\i\.length){2}\))\)/,
|
||||
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
||||
replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)"
|
||||
}
|
||||
|
|
9
src/plugins/fullUserInChatbox/README.md
Normal file
9
src/plugins/fullUserInChatbox/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Full User In Chatbox
|
||||
|
||||
Adds the full user mention to the textbox
|
||||
|
||||
Adds the avatar if you have mentioned avatars enabled
|
||||
|
||||
Provides the full context menu to make it easy to access the users profile, and other common actions
|
||||
|
||||
https://github.com/user-attachments/assets/cd9edb33-99c8-4c8d-b669-8cddd05f4b45
|
47
src/plugins/fullUserInChatbox/index.tsx
Normal file
47
src/plugins/fullUserInChatbox/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
||||
|
||||
interface UserMentionComponentProps {
|
||||
id: string;
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
OriginalComponent: ReactNode;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "FullUserInChatbox",
|
||||
description: "Makes the user mention in the chatbox have more functionalities, like left/right clicking",
|
||||
authors: [Devs.sadan],
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ':"text":',
|
||||
replacement: {
|
||||
match: /(hidePersonalInformation\).+?)(if\(null!=\i\){.+?return \i)(?=})/,
|
||||
replace: "$1return $self.UserMentionComponent({...arguments[0],OriginalComponent:(()=>{$2})()});"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
UserMentionComponent: ErrorBoundary.wrap((props: UserMentionComponentProps) => (
|
||||
<UserMentionComponent
|
||||
// This seems to be constant
|
||||
className="mention"
|
||||
userId={props.id}
|
||||
channelId={props.channelId}
|
||||
/>
|
||||
), {
|
||||
fallback: ({ wrappedProps }) => wrappedProps.OriginalComponent
|
||||
})
|
||||
});
|
|
@ -24,6 +24,7 @@ import { debounce } from "@shared/debounce";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Menu, ReactDOM } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
import type { Root } from "react-dom/client";
|
||||
|
||||
import { Magnifier, MagnifierProps } from "./components/Magnifier";
|
||||
|
|
|
@ -41,6 +41,7 @@ import {
|
|||
UserStore
|
||||
} from "@webpack/common";
|
||||
import { Channel, Message } from "discord-types/general";
|
||||
import { JSX } from "react";
|
||||
|
||||
const messageCache = new Map<string, {
|
||||
message?: Message;
|
||||
|
@ -347,10 +348,10 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
|
|||
? parse(message.content)
|
||||
: [noContent(message.attachments.length, message.embeds.length)]
|
||||
}
|
||||
{images.map(a => {
|
||||
{images.map((a, idx) => {
|
||||
const { width, height } = computeWidthAndHeight(a.width, a.height);
|
||||
return (
|
||||
<div>
|
||||
<div key={idx}>
|
||||
<img src={a.url} width={width} height={height} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -69,6 +69,7 @@ export function HistoryModal({ modalProps, message }: { modalProps: ModalProps;
|
|||
|
||||
{timestamps.map((timestamp, index) => (
|
||||
<TabBar.Item
|
||||
key={index}
|
||||
className="vc-settings-tab-bar-item"
|
||||
id={index}
|
||||
>
|
||||
|
|
|
@ -169,8 +169,8 @@ export default definePlugin({
|
|||
|
||||
return Settings.plugins.MessageLogger.inlineEdits && (
|
||||
<>
|
||||
{message.editHistory?.map(edit => (
|
||||
<div className="messagelogger-edited">
|
||||
{message.editHistory?.map((edit, idx) => (
|
||||
<div key={idx} className="messagelogger-edited">
|
||||
{parseEditContent(edit.content, message)}
|
||||
<Timestamp
|
||||
timestamp={edit.timestamp}
|
||||
|
@ -304,7 +304,7 @@ export default definePlugin({
|
|||
{...props}
|
||||
className={classes("messagelogger-edit-marker", className)}
|
||||
onClick={() => openHistoryModal(message)}
|
||||
aria-role="button"
|
||||
role="button"
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
|
@ -346,35 +346,35 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE event
|
||||
match: /MESSAGE_DELETE:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||
match: /function (?=.+?MESSAGE_DELETE:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
replace:
|
||||
"MESSAGE_DELETE:function($1){" +
|
||||
" var cache = $2getOrCreate($1.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $1, false);" +
|
||||
" $2commit(cache);" +
|
||||
"},"
|
||||
"function $1($2){" +
|
||||
" var cache = $3getOrCreate($2.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $2, false);" +
|
||||
" $3commit(cache);" +
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
|
||||
match: /MESSAGE_DELETE_BULK:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||
match: /function (?=.+?MESSAGE_DELETE_BULK:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
replace:
|
||||
"MESSAGE_DELETE_BULK:function($1){" +
|
||||
" var cache = $2getOrCreate($1.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $1, true);" +
|
||||
" $2commit(cache);" +
|
||||
"},"
|
||||
"function $1($2){" +
|
||||
" var cache = $3getOrCreate($2.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $2, true);" +
|
||||
" $3commit(cache);" +
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Add current cached content + new edit time to cached message's editHistory
|
||||
match: /(MESSAGE_UPDATE:function\((\i)\).+?)\.update\((\i)/,
|
||||
match: /(function (\i)\((\i)\).+?)\.update\((\i)(?=.*MESSAGE_UPDATE:\2)/,
|
||||
replace: "$1" +
|
||||
".update($3,m =>" +
|
||||
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :" +
|
||||
" $2.message.edited_timestamp && $2.message.content !== m.content ?" +
|
||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
||||
".update($4,m =>" +
|
||||
" (($3.message.flags & 64) === 64 || $self.shouldIgnore($3.message, true)) ? m :" +
|
||||
" $3.message.edited_timestamp && $3.message.content !== m.content ?" +
|
||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($3.message, m)]) :" +
|
||||
" m" +
|
||||
")" +
|
||||
".update($3"
|
||||
".update($4"
|
||||
},
|
||||
{
|
||||
// fix up key (edit last message) attempting to edit a deleted message
|
||||
|
@ -488,12 +488,12 @@ export default definePlugin({
|
|||
find: '"ReferencedMessageStore"',
|
||||
replacement: [
|
||||
{
|
||||
match: /MESSAGE_DELETE:function\((\i)\).+?},/,
|
||||
replace: "MESSAGE_DELETE:function($1){},"
|
||||
match: /MESSAGE_DELETE:\i,/,
|
||||
replace: "MESSAGE_DELETE:()=>{},"
|
||||
},
|
||||
{
|
||||
match: /MESSAGE_DELETE_BULK:function\((\i)\).+?},/,
|
||||
replace: "MESSAGE_DELETE_BULK:function($1){},"
|
||||
match: /MESSAGE_DELETE_BULK:\i,/,
|
||||
replace: "MESSAGE_DELETE_BULK:()=>{},"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -114,7 +114,7 @@ function SettingsComponent() {
|
|||
return (
|
||||
<Flex flexDirection="column">
|
||||
{tags.map(t => (
|
||||
<Card style={{ padding: "1em 1em 0" }}>
|
||||
<Card key={t.name} style={{ padding: "1em 1em 0" }}>
|
||||
<Forms.FormTitle style={{ width: "fit-content" }}>
|
||||
<Tooltip text={t.description}>
|
||||
{({ onMouseEnter, onMouseLeave }) => (
|
||||
|
@ -183,8 +183,8 @@ export default definePlugin({
|
|||
{
|
||||
find: ".ORIGINAL_POSTER=",
|
||||
replacement: {
|
||||
match: /\((\i)=\{\}\)\)\[(\i)\.BOT/,
|
||||
replace: "($1=$self.getTagTypes()))[$2.BOT"
|
||||
match: /(?=(\i)\[\i\.BOT)/,
|
||||
replace: "$self.genTagTypes($1);"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -280,8 +280,7 @@ export default definePlugin({
|
|||
.filter(Boolean);
|
||||
},
|
||||
|
||||
getTagTypes() {
|
||||
const obj = {};
|
||||
genTagTypes(obj) {
|
||||
let i = 100;
|
||||
tags.forEach(({ name }) => {
|
||||
obj[name] = ++i;
|
||||
|
@ -291,7 +290,6 @@ export default definePlugin({
|
|||
obj[`${name}-OP`] = ++i;
|
||||
obj[i] = `${name}-OP`;
|
||||
});
|
||||
return obj;
|
||||
},
|
||||
|
||||
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
|
||||
|
|
|
@ -24,6 +24,7 @@ import definePlugin from "@utils/types";
|
|||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
|
||||
import { Channel, User } from "discord-types/general";
|
||||
import { JSX } from "react";
|
||||
|
||||
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
|
||||
const UserUtils = findByPropsLazy("getGlobalName");
|
||||
|
@ -55,6 +56,7 @@ function getMutualGDMCountText(user: User) {
|
|||
function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) {
|
||||
return mutualDms.map(c => (
|
||||
<Clickable
|
||||
key={c.id}
|
||||
className={ProfileListClasses.listRow}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
findGroupChildrenByChildId,
|
||||
NavContextMenuPatchCallback
|
||||
} from "@api/ContextMenu";
|
||||
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { CogWheel } from "@components/Icons";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
@ -115,8 +115,6 @@ function applyDefaultSettings(guildId: string | null) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
migratePluginSettings("NewGuildSettings", "MuteNewGuild");
|
||||
export default definePlugin({
|
||||
name: "NewGuildSettings",
|
||||
description: "Automatically mute new servers and change various other settings upon joining",
|
||||
|
|
|
@ -54,8 +54,8 @@ export default definePlugin({
|
|||
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
|
||||
replace: (_, props) => `if($self.isBlocked(${props}.message))return;`
|
||||
match: /(?<=function (\i)\((\i)\){)(?=.*MESSAGE_CREATE:\1)/,
|
||||
replace: (_, _funcName, props) => `if($self.isBlocked(${props}.message))return;`
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
|
|
@ -74,10 +74,10 @@ export default definePlugin({
|
|||
// This prevents the Message Requests tab from always hiding due to the previous patch (and is compatible with spam requests)
|
||||
// In short, only the red badge is hidden. Button visibility behavior isn't changed.
|
||||
{
|
||||
find: ".getSpamChannelsCount()",
|
||||
find: ".getSpamChannelsCount();return",
|
||||
predicate: () => settings.store.hideMessageRequestsCount,
|
||||
replacement: {
|
||||
match: /(?<=getSpamChannelsCount\(\),\i=)\i\.getMessageRequestsCount\(\)/,
|
||||
match: /(?<=getSpamChannelsCount\(\);return )\i\.getMessageRequestsCount\(\)/,
|
||||
replace: "$self.getRealMessageRequestCount()"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
@ -28,11 +27,16 @@ export default definePlugin({
|
|||
name: "NoScreensharePreview",
|
||||
description: "Disables screenshare previews from being sent.",
|
||||
authors: [Devs.Nuckyz],
|
||||
|
||||
start() {
|
||||
if (!DisableStreamPreviews.getSetting()) {
|
||||
DisableStreamPreviews.updateSetting(true);
|
||||
}
|
||||
},
|
||||
|
||||
Settings.plugins.NoScreensharePreview.enabled = false;
|
||||
stop() {
|
||||
if (DisableStreamPreviews.getSetting()) {
|
||||
DisableStreamPreviews.updateSetting(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
5
src/plugins/noUnblockToJump/README.md
Normal file
5
src/plugins/noUnblockToJump/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# No Unblock To Jump
|
||||
|
||||
Removes the popup preventing you to jump to a message from a blocked/ignored user (eg: in search results)
|
||||
|
||||
![A modal popup telling you to unblock a user to jump their message](https://github.com/user-attachments/assets/0e4b859d-f3b3-4101-9a83-829afb473d1e)
|
|
@ -26,25 +26,46 @@ export default definePlugin({
|
|||
authors: [Devs.dzshn],
|
||||
patches: [
|
||||
{
|
||||
// Clicking on search results to jump
|
||||
find: '.id,"Search Results"',
|
||||
replacement: {
|
||||
replacement: [
|
||||
{
|
||||
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
|
||||
replace: "if(false)$1"
|
||||
}
|
||||
},
|
||||
{
|
||||
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNIGNORE_TO_JUMP_TITLE})/,
|
||||
replace: "if(false)$1"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
// Jump buttton in top right corner of messages
|
||||
find: "renderJumpButton()",
|
||||
replacement: {
|
||||
replacement: [
|
||||
{
|
||||
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
|
||||
replace: "if(false)$1"
|
||||
}
|
||||
},
|
||||
{
|
||||
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNIGNORE_TO_JUMP_TITLE})/,
|
||||
replace: "if(false)$1"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
// Clicking on replied messages to jump
|
||||
find: "flash:!0,returnMessageId",
|
||||
replacement: {
|
||||
replacement: [
|
||||
{
|
||||
match: /.\?(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
|
||||
replace: "false?$1"
|
||||
}
|
||||
},
|
||||
{
|
||||
match: /.\?(.{1,10}\.show\({.{1,50}#{intl::UNIGNORE_TO_JUMP_TITLE})/,
|
||||
replace: "false?$1"
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
|
|
@ -57,7 +57,7 @@ const UrlReplacementRules: Record<string, URLReplacementRule> = {
|
|||
description: "Open Tidal links in the Tidal app",
|
||||
},
|
||||
itunes: {
|
||||
match: /^https:\/\/music\.apple\.com\/([a-z]{2}\/)?(album|artist|playlist|song|curator)\/([^/?#]+)\/?([^/?#]+)?(?:\?.*)?(?:#.*)?$/,
|
||||
match: /^https:\/\/(?:geo\.)?music\.apple\.com\/([a-z]{2}\/)?(album|artist|playlist|song|curator)\/([^/?#]+)\/?([^/?#]+)?(?:\?.*)?(?:#.*)?$/,
|
||||
replace: (_, lang, type, name, id) => id ? `itunes://music.apple.com/us/${type}/${name}/${id}` : `itunes://music.apple.com/us/${type}/${name}`,
|
||||
description: "Open Apple Music links in the iTunes app"
|
||||
},
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
|
||||
import { FluxDispatcher } from "@webpack/common";
|
||||
|
@ -41,7 +41,6 @@ const settings = definePluginSettings({
|
|||
},
|
||||
});
|
||||
|
||||
migratePluginSettings("PartyMode", "Party mode 🎉");
|
||||
export default definePlugin({
|
||||
name: "PartyMode",
|
||||
description: "Allows you to use party mode cause the party never ends ✨",
|
||||
|
|
|
@ -113,6 +113,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={cl("modal-list-item-btn")}
|
||||
onClick={() => selectItem(index)}
|
||||
role="button"
|
||||
|
@ -178,7 +179,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
|||
<div className={cl("modal-divider")} />
|
||||
<ScrollerThin className={cl("modal-perms")} orientation="auto">
|
||||
{Object.values(PermissionsBits).map(bit => (
|
||||
<div className={cl("modal-perms-item")}>
|
||||
<div key={bit} className={cl("modal-perms-item")}>
|
||||
<div className={cl("modal-perms-item-icon")}>
|
||||
{(() => {
|
||||
const { permissions, overwriteAllow, overwriteDeny } = selectedItem;
|
||||
|
|
|
@ -192,6 +192,7 @@ function UserPermissionsComponent({ guild, guildMember, closePopout }: { guild:
|
|||
<div className={classes(RoleRootClasses.root)}>
|
||||
{userPermissions.map(({ permission, roleColor, roleName }) => (
|
||||
<Tooltip
|
||||
key={permission}
|
||||
text={<GrantedByTooltip roleName={roleName} roleColor={roleColor} />}
|
||||
tooltipClassName={cl("granted-by-container")}
|
||||
tooltipContentClassName={cl("granted-by-content")}
|
||||
|
|
|
@ -30,13 +30,13 @@ export default definePlugin({
|
|||
{
|
||||
find: ".removeMosaicItemHoverButton),",
|
||||
replacement: {
|
||||
match: /\.nonMediaMosaicItem\]:.{0,40}children:\[(?<=showDownload:(\i).+?isVisualMediaType:(\i).+?)/,
|
||||
replace: "$&$1&&$2&&$self.renderPiPButton(),"
|
||||
match: /(\.nonMediaMosaicItem\]:.{0,40}children:)(\i.slice\(\i\))(?<=showDownload:(\i).+?isVisualMediaType:(\i).+?)/,
|
||||
replace: (_, rest, origChildren, showDownload, isVisualMediaType) => `${rest}[${showDownload}&&${isVisualMediaType}&&$self.PictureInPictureButton(),...${origChildren}]`
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
renderPiPButton: ErrorBoundary.wrap(() => {
|
||||
PictureInPictureButton: ErrorBoundary.wrap(() => {
|
||||
return (
|
||||
<Tooltip text="Toggle Picture in Picture">
|
||||
{tooltipProps => (
|
||||
|
|
|
@ -33,7 +33,8 @@ function createPinMenuItem(channelId: string) {
|
|||
{
|
||||
categories.map(category => (
|
||||
<Menu.MenuItem
|
||||
id={`pin-category-${category.name}`}
|
||||
key={category.id}
|
||||
id={`pin-category-${category.id}`}
|
||||
label={category.name}
|
||||
action={() => addChannelToCategory(channelId, category.id).then(forceUpdate)}
|
||||
/>
|
||||
|
|
|
@ -71,7 +71,7 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Filter out pinned channels from the private channel list
|
||||
match: /(?<=\i,{channels:\i,)privateChannelIds:(\i)/,
|
||||
match: /(?<=channels:\i,)privateChannelIds:(\i)(?=,listRef:)/,
|
||||
replace: "privateChannelIds:$1.filter(c=>!$self.isPinned(c))"
|
||||
},
|
||||
{
|
||||
|
@ -96,8 +96,8 @@ export default definePlugin({
|
|||
|
||||
// Fix Row Height
|
||||
{
|
||||
match: /(?<="getRowHeight",.{1,100}return 1===)\i/,
|
||||
replace: "($&-$self.categoryLen())"
|
||||
match: /(\.startsWith\("section-divider"\).+?return 1===)(\i)/,
|
||||
replace: "$1($2-$self.categoryLen())"
|
||||
},
|
||||
{
|
||||
match: /"getRowHeight",\((\i),(\i)\)=>{/,
|
||||
|
@ -124,7 +124,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".FRIENDS},\"friends\"",
|
||||
replacement: {
|
||||
match: /(?<=\i=\i=>{).{1,100}premiumTabSelected.{1,800}showDMHeader:.+?,/,
|
||||
match: /(?<=\i=\i=>{).{1,100}premiumTabSelected.{0,950}showDMHeader:.+?,/,
|
||||
replace: "let forceUpdate = Vencord.Util.useForceUpdater();$&_forceUpdate:forceUpdate,"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -20,7 +20,7 @@ import { definePluginSettings, Settings } from "@api/Settings";
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { ChannelStore, ComponentDispatch, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
|
||||
const Kangaroo = findByPropsLazy("jumpToMessage");
|
||||
|
@ -60,24 +60,24 @@ export default definePlugin({
|
|||
settings,
|
||||
|
||||
start() {
|
||||
Dispatcher.subscribe("DELETE_PENDING_REPLY", onDeletePendingReply);
|
||||
Dispatcher.subscribe("MESSAGE_END_EDIT", onEndEdit);
|
||||
Dispatcher.subscribe("MESSAGE_START_EDIT", onStartEdit);
|
||||
Dispatcher.subscribe("CREATE_PENDING_REPLY", onCreatePendingReply);
|
||||
document.addEventListener("keydown", onKeydown);
|
||||
},
|
||||
|
||||
stop() {
|
||||
Dispatcher.unsubscribe("DELETE_PENDING_REPLY", onDeletePendingReply);
|
||||
Dispatcher.unsubscribe("MESSAGE_END_EDIT", onEndEdit);
|
||||
Dispatcher.unsubscribe("MESSAGE_START_EDIT", onStartEdit);
|
||||
Dispatcher.unsubscribe("CREATE_PENDING_REPLY", onCreatePendingReply);
|
||||
document.removeEventListener("keydown", onKeydown);
|
||||
},
|
||||
});
|
||||
|
||||
const onDeletePendingReply = () => replyIdx = -1;
|
||||
const onEndEdit = () => editIdx = -1;
|
||||
flux: {
|
||||
DELETE_PENDING_REPLY() {
|
||||
replyIdx = -1;
|
||||
},
|
||||
MESSAGE_END_EDIT() {
|
||||
editIdx = -1;
|
||||
},
|
||||
MESSAGE_START_EDIT: onStartEdit,
|
||||
CREATE_PENDING_REPLY: onCreatePendingReply
|
||||
}
|
||||
});
|
||||
|
||||
function calculateIdx(messages: Message[], id: string) {
|
||||
const idx = messages.findIndex(m => m.id === id);
|
||||
|
@ -109,6 +109,8 @@ function onKeydown(e: KeyboardEvent) {
|
|||
if (!isUp && e.key !== "ArrowDown") return;
|
||||
if (!isCtrl(e) || isAltOrMeta(e)) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (e.shiftKey)
|
||||
nextEdit(isUp);
|
||||
else
|
||||
|
@ -194,9 +196,10 @@ function nextReply(isUp: boolean) {
|
|||
channel,
|
||||
message,
|
||||
shouldMention: shouldMention(message),
|
||||
showMentionToggle: channel.guild_id !== null && message.author.id !== meId,
|
||||
showMentionToggle: channel.isPrivate() && message.author.id !== meId,
|
||||
_isQuickReply: true
|
||||
});
|
||||
ComponentDispatch.dispatchToLastSubscribed("TEXTAREA_FOCUS");
|
||||
jumpIfOffScreen(channel.id, message.id);
|
||||
}
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ export default LazyComponent(() => {
|
|||
onClick={() => openBlockModal()}
|
||||
/>
|
||||
)}
|
||||
{review.sender.badges.map(badge => <ReviewBadge {...badge} />)}
|
||||
{review.sender.badges.map((badge, idx) => <ReviewBadge key={idx} {...badge} />)}
|
||||
|
||||
{
|
||||
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
|
||||
|
@ -170,7 +170,13 @@ export default LazyComponent(() => {
|
|||
|
||||
<div className={cl("review-comment")}>
|
||||
{(review.comment.length > 200 && !showAll)
|
||||
? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...", <br />, (<a onClick={() => setShowAll(true)}>Read more</a>)]
|
||||
? (
|
||||
<>
|
||||
{Parser.parseGuildEventDescription(review.comment.substring(0, 200))}...
|
||||
<br />
|
||||
<a onClick={() => setShowAll(true)}>Read more</a>]
|
||||
</>
|
||||
)
|
||||
: Parser.parseGuildEventDescription(review.comment)}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ const ChatBarIcon: ChatBarButton = ({ isMainChat }) => {
|
|||
viewBox="0 0 24 24"
|
||||
style={{ scale: "1.2" }}
|
||||
>
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<g fill="none" fillRule="evenodd">
|
||||
<path fill="currentColor" d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z" />
|
||||
<rect width="24" height="24" />
|
||||
</g>
|
||||
|
|
|
@ -247,6 +247,7 @@ function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setC
|
|||
<ScrollerThin fade className={cl("scroller")}>
|
||||
{members.map(id =>
|
||||
<FriendRow
|
||||
key={id}
|
||||
user={UserStore.getUser(id)}
|
||||
status={PresenceStore.getStatus(id) || "offline"}
|
||||
onSelect={() => openUserProfile(id)}
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
@ -25,7 +24,6 @@ const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild;
|
|||
);
|
||||
};
|
||||
|
||||
migratePluginSettings("ServerInfo", "ServerProfile"); // what was I thinking with this name lmao
|
||||
export default definePlugin({
|
||||
name: "ServerInfo",
|
||||
description: "Allows you to view info about a server",
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { Clipboard } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { cl } from "../utils/misc";
|
||||
import { CopyButton } from "./CopyButton";
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import type { IThemedToken } from "@vap/shiki";
|
||||
import { hljs } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import { cl } from "../utils/misc";
|
||||
import { ThemeBase } from "./Highlighter";
|
||||
|
@ -41,12 +42,12 @@ export const Code = ({
|
|||
|
||||
if (useHljs) {
|
||||
try {
|
||||
const { value: hljsHtml } = hljs.highlight(lang!, content, true);
|
||||
const { value: hljsHtml } = hljs.highlight(content, { language: lang!, ignoreIllegals: true });
|
||||
lines = hljsHtml
|
||||
.split("\n")
|
||||
.map((line, i) => <span key={i} dangerouslySetInnerHTML={{ __html: line }} />);
|
||||
} catch {
|
||||
lines = content.split("\n").map(line => <span>{line}</span>);
|
||||
lines = content.split("\n").map((line, idx) => <span key={idx}>{line}</span>);
|
||||
}
|
||||
} else {
|
||||
const renderTokens =
|
||||
|
@ -55,11 +56,11 @@ export const Code = ({
|
|||
.split("\n")
|
||||
.map(line => [{ color: theme.plainColor, content: line } as IThemedToken]);
|
||||
|
||||
lines = renderTokens.map(line => {
|
||||
lines = renderTokens.map((line, idx) => {
|
||||
// [Cynthia] this makes it so when you highlight the codeblock
|
||||
// empty lines are also selected and copied when you Ctrl+C.
|
||||
if (line.length === 0) {
|
||||
return <span>{"\n"}</span>;
|
||||
return <span key={idx}>{"\n"}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -97,7 +97,7 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {
|
|||
gap: getSpacingPx(settings.store.iconSpacing),
|
||||
flexWrap: "wrap"
|
||||
}}>
|
||||
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} />)}
|
||||
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} key={connection.id} />)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
@ -137,6 +137,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect
|
|||
className="vc-user-connection"
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={e => {
|
||||
if (Vencord.Plugins.isPluginEnabled("OpenInApp")) {
|
||||
const OpenInApp = Vencord.Plugins.plugins.OpenInApp as any as typeof import("../openInApp").default;
|
||||
|
|
|
@ -268,7 +268,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
|
|||
<div className="shc-lock-screen-tags-container">
|
||||
<Text variant="text-lg/bold">Available tags:</Text>
|
||||
<div className="shc-lock-screen-tags">
|
||||
{availableTags.map(tag => <TagComponent tag={tag} />)}
|
||||
{availableTags.map(tag => <TagComponent tag={tag} key={tag.id} />)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ export default definePlugin({
|
|||
},
|
||||
// Add the hidden eye icon if the channel is hidden
|
||||
{
|
||||
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||
match: /\.name,{.{0,140}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null`
|
||||
},
|
||||
// Make voice channels also appear as muted if they are muted
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { definePluginSettings, migratePluginSettings } from "@api/Settings";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType, PluginSettingDef } from "@utils/types";
|
||||
|
||||
|
@ -30,17 +30,16 @@ const opt = (description: string) => ({
|
|||
const settings = definePluginSettings({
|
||||
showTimeouts: opt("Show member timeout icons in chat."),
|
||||
showInvitesPaused: opt("Show the invites paused tooltip in the server list."),
|
||||
showModView: opt("Show the member mod view context menu item in all servers."),
|
||||
disableDiscoveryFilters: opt("Disable filters in Server Discovery search that hide servers that don't meet discovery criteria."),
|
||||
disableDisallowedDiscoveryFilters: opt("Disable filters in Server Discovery search that hide NSFW & disallowed servers."),
|
||||
showModView: opt("Show the member mod view context menu item in all servers.")
|
||||
});
|
||||
|
||||
migratePluginSettings("ShowHiddenThings", "ShowTimeouts");
|
||||
export default definePlugin({
|
||||
name: "ShowHiddenThings",
|
||||
tags: ["ShowTimeouts", "ShowInvitesPaused", "ShowModView", "DisableDiscoveryFilters"],
|
||||
description: "Displays various hidden & moderator-only things regardless of permissions.",
|
||||
authors: [Devs.Dolfies],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "showCommunicationDisabledStyles",
|
||||
|
@ -83,43 +82,6 @@ export default definePlugin({
|
|||
match: /\i(?=\?null)/,
|
||||
replace: "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "prod_discoverable_guilds",
|
||||
predicate: () => settings.store.disableDiscoveryFilters,
|
||||
replacement: {
|
||||
match: /\{"auto_removed:.*?\}/,
|
||||
replace: "{}"
|
||||
}
|
||||
},
|
||||
// remove the 200 server minimum
|
||||
{
|
||||
find: '">200"',
|
||||
predicate: () => settings.store.disableDiscoveryFilters,
|
||||
replacement: {
|
||||
match: '">200"',
|
||||
replace: '">0"'
|
||||
}
|
||||
},
|
||||
// empty word filter
|
||||
{
|
||||
find: '"pepe","nude"',
|
||||
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
||||
replacement: {
|
||||
match: /(?<=[?=])\["pepe",.+?\]/,
|
||||
replace: "[]",
|
||||
},
|
||||
},
|
||||
// patch request that queries if term is allowed
|
||||
{
|
||||
find: ".GUILD_DISCOVERY_VALID_TERM,query:",
|
||||
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
||||
all: true,
|
||||
replacement: {
|
||||
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0,rejectWithError:!1\}\)/g,
|
||||
replace: "Promise.resolve({ body: { valid: true } })"
|
||||
}
|
||||
}
|
||||
],
|
||||
settings,
|
||||
]
|
||||
});
|
||||
|
|
|
@ -84,13 +84,12 @@ export default definePlugin({
|
|||
],
|
||||
|
||||
TooltipWrapper: ErrorBoundary.wrap(({ message, children, text }: { message: Message; children: FunctionComponent<any>; text: ReactNode; }) => {
|
||||
if (settings.store.displayStyle === DisplayStyle.Tooltip) return <Tooltip
|
||||
children={children}
|
||||
text={renderTimeout(message, false)}
|
||||
/>;
|
||||
if (settings.store.displayStyle === DisplayStyle.Tooltip)
|
||||
return <Tooltip text={renderTimeout(message, false)}>{children}</Tooltip>;
|
||||
|
||||
return (
|
||||
<div className="vc-std-wrapper">
|
||||
<Tooltip text={text} children={children} />
|
||||
<Tooltip text={text}>{children}</Tooltip>
|
||||
<Text variant="text-md/normal" color="status-danger">
|
||||
{renderTimeout(message, true)} timeout remaining
|
||||
</Text>
|
||||
|
|
|
@ -78,7 +78,7 @@ const SilentMessageToggle: ChatBarButton = ({ isMainChat }) => {
|
|||
{!enabled && <>
|
||||
<mask id="vc-silent-msg-mask">
|
||||
<path fill="#fff" d="M0 0h24v24H0Z" />
|
||||
<path stroke="#000" stroke-width="5.99068" d="M0 24 24 0" />
|
||||
<path stroke="#000" strokeWidth="5.99068" d="M0 24 24 0" />
|
||||
</mask>
|
||||
<path fill="var(--status-danger)" d="m21.178 1.70703 1.414 1.414L4.12103 21.593l-1.414-1.415L21.178 1.70703Z" />
|
||||
</>}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
import "./spotifyStyles.css";
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { ImageIcon, LinkIcon, OpenExternalIcon } from "@components/Icons";
|
||||
import { debounce } from "@shared/debounce";
|
||||
|
@ -130,7 +131,9 @@ function Controls() {
|
|||
>
|
||||
<Shuffle />
|
||||
</Button>
|
||||
<Button onClick={() => SpotifyStore.prev()}>
|
||||
<Button onClick={() => {
|
||||
Settings.plugins.SpotifyControls.previousButtonRestartsTrack && SpotifyStore.position > 3000 ? SpotifyStore.seek(0) : SpotifyStore.prev();
|
||||
}}>
|
||||
<SkipPrev />
|
||||
</Button>
|
||||
<Button onClick={() => SpotifyStore.setPlaying(!isPlaying)}>
|
||||
|
|
|
@ -44,6 +44,11 @@ export default definePlugin({
|
|||
type: OptionType.BOOLEAN,
|
||||
description: "Open Spotify URIs instead of Spotify URLs. Will only work if you have Spotify installed and might not work on all platforms",
|
||||
default: false
|
||||
},
|
||||
previousButtonRestartsTrack: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Restart currently playing track when pressing the previous button if playtime is >3s",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
patches: [
|
||||
|
|
|
@ -120,8 +120,8 @@ function ServerTrace({ trace }: ServerTraceProps) {
|
|||
<Forms.FormSection title="Server Trace" tag="h2">
|
||||
<code>
|
||||
<Flex flexDirection="column" style={{ color: "var(--header-primary)", gap: 5, userSelect: "text" }}>
|
||||
{lines.map(line => (
|
||||
<span>{line}</span>
|
||||
{lines.map((line, idx) => (
|
||||
<span key={idx}>{line}</span>
|
||||
))}
|
||||
</Flex>
|
||||
</code>
|
||||
|
|
|
@ -163,7 +163,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "UNREAD_IMPORTANT:",
|
||||
replacement: {
|
||||
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||
match: /\.name,{.{0,140}\.children.+?:null(?<=,channel:(\i).+?)/,
|
||||
replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -23,6 +23,7 @@ import { openUserProfile } from "@utils/discord";
|
|||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
import { PropsWithChildren } from "react";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
showAvatars: {
|
||||
|
@ -91,34 +92,31 @@ export default definePlugin({
|
|||
name: "TypingTweaks",
|
||||
description: "Show avatars and role colours in the typing indicator",
|
||||
authors: [Devs.zt],
|
||||
settings,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::THREE_USERS_TYPING}",
|
||||
replacement: [
|
||||
{
|
||||
// Style the indicator and add function call to modify the children before rendering
|
||||
{
|
||||
find: "getCooldownTextStyle",
|
||||
replacement: {
|
||||
match: /(?<=children:\[(\i)\.length>0.{0,200}?"aria-atomic":!0,children:)\i/,
|
||||
replace: "$self.mutateChildren(this.props, $1, $&), style: $self.TYPING_TEXT_STYLE"
|
||||
}
|
||||
match: /(?<=children:\[(\i)\.length>0.{0,200}?"aria-atomic":!0,children:)\i(?<=guildId:(\i).+?)/,
|
||||
replace: "$self.renderTypingUsers({ users: $1, guildId: $2, children: $& }),style:$self.TYPING_TEXT_STYLE"
|
||||
},
|
||||
{
|
||||
// Changes the indicator to keep the user object when creating the list of typing users
|
||||
{
|
||||
find: "getCooldownTextStyle",
|
||||
replacement: {
|
||||
match: /(?<=map\(\i=>)\i\.\i\.getName\(\i,this\.props\.channel\.id,(\i)\)/,
|
||||
replace: "$1"
|
||||
}
|
||||
match: /\.map\((\i)=>\i\.\i\.getName\(\i,\i\.id,\1\)\)/,
|
||||
replace: ""
|
||||
},
|
||||
{
|
||||
// Adds the alternative formatting for several users typing
|
||||
{
|
||||
find: "getCooldownTextStyle",
|
||||
replacement: {
|
||||
match: /(,{a:(\i),b:(\i),c:\i}\):)\i\.\i\.string\(\i\.\i#{intl::SEVERAL_USERS_TYPING}\)(?<=(\i)\.length.+?)/,
|
||||
replace: (_, rest, a, b, users) => `${rest}$self.buildSeveralUsers({ a: ${a}, b: ${b}, count: ${users}.length - 2 })`
|
||||
},
|
||||
match: /(,{a:(\i),b:(\i),c:\i}\):\i\.length>3&&\(\i=)\i\.\i\.string\(\i\.\i#{intl::SEVERAL_USERS_TYPING}\)(?<=(\i)\.length.+?)/,
|
||||
replace: (_, rest, a, b, users) => `${rest}$self.buildSeveralUsers({ a: ${a}, b: ${b}, count: ${users}.length - 2 })`,
|
||||
predicate: () => settings.store.alternativeFormatting
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
settings,
|
||||
|
||||
TYPING_TEXT_STYLE: {
|
||||
display: "grid",
|
||||
|
@ -128,7 +126,7 @@ export default definePlugin({
|
|||
|
||||
buildSeveralUsers,
|
||||
|
||||
mutateChildren(props: any, users: User[], children: any) {
|
||||
renderTypingUsers: ErrorBoundary.wrap(({ guildId, users, children }: PropsWithChildren<{ guildId: string, users: User[]; }>) => {
|
||||
try {
|
||||
if (!Array.isArray(children)) {
|
||||
return children;
|
||||
|
@ -136,15 +134,17 @@ export default definePlugin({
|
|||
|
||||
let element = 0;
|
||||
|
||||
return children.map(c =>
|
||||
c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c))
|
||||
? <TypingUser {...props} user={users[element++]} />
|
||||
: c
|
||||
);
|
||||
return children.map(c => {
|
||||
if (c.type !== "strong" && !(typeof c !== "string" && !React.isValidElement(c)))
|
||||
return c;
|
||||
|
||||
const user = users[element++];
|
||||
return <TypingUser key={user.id} guildId={guildId} user={user} />;
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
}, { noop: true })
|
||||
});
|
||||
|
|
|
@ -25,6 +25,7 @@ import { wordsToTitle } from "@utils/text";
|
|||
import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
|
||||
import { ReactElement } from "react";
|
||||
|
||||
interface VoiceState {
|
||||
userId: string;
|
||||
|
@ -289,7 +290,7 @@ export default definePlugin({
|
|||
description: "Undeafen Message (only self for now)",
|
||||
default: "{{USER}} undeafened"
|
||||
}
|
||||
};
|
||||
} satisfies Record<string, PluginOptionsItem>;
|
||||
},
|
||||
|
||||
settingsAboutComponent({ tempSettings: s }) {
|
||||
|
@ -303,7 +304,7 @@ export default definePlugin({
|
|||
[],
|
||||
);
|
||||
|
||||
let errorComponent: React.ReactElement | null = null;
|
||||
let errorComponent: ReactElement<any> | null = null;
|
||||
if (!hasVoices) {
|
||||
let error = "No narrator voices found. ";
|
||||
error += navigator.platform?.toLowerCase().includes("linux")
|
||||
|
|
|
@ -94,7 +94,7 @@ export default definePlugin({
|
|||
find: "AudioContextSettingsMigrated",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=isLocalMute\(\i,\i\),volume:.+?volume:)\i(?=})/,
|
||||
match: /(?<=isLocalMute\(\i,\i\),volume:(\i).+?\i\(\i,\i,)\1(?=\))/,
|
||||
replace: "$&>200?200:$&"
|
||||
},
|
||||
{
|
||||
|
@ -109,7 +109,7 @@ export default definePlugin({
|
|||
},
|
||||
// Prevent the MediaEngineStore from overwriting our LocalVolumes above 200 with the ones the Discord Audio Context Settings sync sends
|
||||
{
|
||||
find: '"MediaEngineStore"',
|
||||
find: '="MediaEngineStore",',
|
||||
replacement: [
|
||||
{
|
||||
match: /(\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/,
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Queue } from "@utils/Queue";
|
|||
import { useForceUpdater } from "@utils/react";
|
||||
import definePlugin from "@utils/types";
|
||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common";
|
||||
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip, useEffect, useLayoutEffect } from "@webpack/common";
|
||||
import { CustomEmoji } from "@webpack/types";
|
||||
import { Message, ReactionEmoji, User } from "discord-types/general";
|
||||
|
||||
|
@ -113,8 +113,8 @@ export default definePlugin({
|
|||
{
|
||||
find: '"MessageReactionsStore"',
|
||||
replacement: {
|
||||
match: /(?<=CONNECTION_OPEN:function\(\){)(\i)={}/,
|
||||
replace: "$&;$self.reactions=$1"
|
||||
match: /function (\i)\(\){(\i)={}(?=.*CONNECTION_OPEN:\1)/,
|
||||
replace: "$&;$self.reactions=$2;"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -134,18 +134,21 @@ export default definePlugin({
|
|||
renderUsers(props: RootObject) {
|
||||
return props.message.reactions.length > 10 ? null : (
|
||||
<ErrorBoundary noop>
|
||||
<this._renderUsers {...props} />
|
||||
<this.UsersComponent {...props} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
},
|
||||
_renderUsers({ message, emoji, type }: RootObject) {
|
||||
|
||||
UsersComponent({ message, emoji, type }: RootObject) {
|
||||
const forceUpdate = useForceUpdater();
|
||||
React.useLayoutEffect(() => { // bc need to prevent autoscrolling
|
||||
|
||||
useLayoutEffect(() => { // bc need to prevent autoscrolling
|
||||
if (Scroll?.scrollCounter > 0) {
|
||||
Scroll.setAutomaticAnchor(null);
|
||||
}
|
||||
});
|
||||
React.useEffect(() => {
|
||||
|
||||
useEffect(() => {
|
||||
const cb = (e: any) => {
|
||||
if (e.messageId === message.id)
|
||||
forceUpdate();
|
||||
|
@ -153,7 +156,7 @@ export default definePlugin({
|
|||
FluxDispatcher.subscribe("MESSAGE_REACTION_ADD_USERS", cb);
|
||||
|
||||
return () => FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD_USERS", cb);
|
||||
}, [message.id]);
|
||||
}, [message.id, forceUpdate]);
|
||||
|
||||
const reactions = getReactionsWithQueue(message, emoji, type);
|
||||
const users = Object.values(reactions).filter(Boolean) as User[];
|
||||
|
|
|
@ -4,12 +4,10 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { migratePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
// The entire code of this plugin can be found in native.ts
|
||||
migratePluginSettings("YoutubeAdblock", "WatchTogetherAdblock");
|
||||
export default definePlugin({
|
||||
name: "YoutubeAdblock",
|
||||
description: "Block ads in YouTube embeds and the WatchTogether activity via AdGuard",
|
||||
|
|
|
@ -47,7 +47,7 @@ export interface ModalOptions {
|
|||
onCloseCallback?: (() => void);
|
||||
}
|
||||
|
||||
type RenderFunction = (props: ModalProps) => ReactNode;
|
||||
type RenderFunction = (props: ModalProps) => ReactNode | Promise<ReactNode>;
|
||||
|
||||
export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as {
|
||||
ModalRoot: ComponentType<PropsWithChildren<{
|
||||
|
@ -141,35 +141,32 @@ export const ModalContent = LazyComponent(() => Modals.ModalContent);
|
|||
export const ModalFooter = LazyComponent(() => Modals.ModalFooter);
|
||||
export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton);
|
||||
|
||||
const ModalAPI = findByPropsLazy("openModalLazy");
|
||||
export const ModalAPI = findByPropsLazy("openModalLazy");
|
||||
|
||||
/**
|
||||
* Wait for the render promise to resolve, then open a modal with it.
|
||||
* This is equivalent to render().then(openModal)
|
||||
* You should use the Modal components exported by this file
|
||||
*/
|
||||
export function openModalLazy(render: () => Promise<RenderFunction>, options?: ModalOptions & { contextKey?: string; }): Promise<string> {
|
||||
return ModalAPI.openModalLazy(render, options);
|
||||
}
|
||||
export const openModalLazy: (render: () => Promise<RenderFunction>, options?: ModalOptions & { contextKey?: string; }) => Promise<string>
|
||||
= proxyLazyWebpack(() => ModalAPI.openModalLazy);
|
||||
|
||||
/**
|
||||
* Open a Modal with the given render function.
|
||||
* You should use the Modal components exported by this file
|
||||
*/
|
||||
export function openModal(render: RenderFunction, options?: ModalOptions, contextKey?: string): string {
|
||||
return ModalAPI.openModal(render, options, contextKey);
|
||||
}
|
||||
export const openModal: (render: RenderFunction, options?: ModalOptions, contextKey?: string) => string
|
||||
= proxyLazyWebpack(() => ModalAPI.openModal);
|
||||
|
||||
/**
|
||||
* Close a modal by its key
|
||||
*/
|
||||
export function closeModal(modalKey: string, contextKey?: string): void {
|
||||
return ModalAPI.closeModal(modalKey, contextKey);
|
||||
}
|
||||
export const closeModal: (modalKey: string, contextKey?: string) => void
|
||||
= proxyLazyWebpack(() => ModalAPI.closeModal);
|
||||
|
||||
/**
|
||||
* Close all open modals
|
||||
*/
|
||||
export function closeAllModals(): void {
|
||||
return ModalAPI.closeAllModals();
|
||||
}
|
||||
export const closeAllModals: () => void
|
||||
= proxyLazyWebpack(() => ModalAPI.closeAllModals);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
|
||||
import { ActionDispatch } from "react";
|
||||
|
||||
import { checkIntersecting } from "./misc";
|
||||
|
||||
|
@ -117,8 +118,8 @@ export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterO
|
|||
/**
|
||||
* Returns a function that can be used to force rerender react components
|
||||
*/
|
||||
export function useForceUpdater(): () => void;
|
||||
export function useForceUpdater(withDep: true): [unknown, () => void];
|
||||
export function useForceUpdater(): ActionDispatch<[]>;
|
||||
export function useForceUpdater(withDep: true): [any, ActionDispatch<[]>];
|
||||
export function useForceUpdater(withDep?: true) {
|
||||
const r = useReducer(x => x + 1, 0);
|
||||
return withDep ? r : r[1];
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import { Command } from "@api/Commands";
|
||||
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { FluxEvents } from "@webpack/types";
|
||||
import { JSX } from "react";
|
||||
import { Promisable } from "type-fest";
|
||||
|
||||
// exists to export default definePlugin({...})
|
||||
|
|
|
@ -48,7 +48,7 @@ export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', {
|
|||
export const RestAPI: t.RestAPI = findLazy(m => typeof m === "object" && m.del && m.put);
|
||||
export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear");
|
||||
|
||||
export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage");
|
||||
export const hljs: typeof import("highlight.js").default = findByPropsLazy("highlight", "registerLanguage");
|
||||
|
||||
export const { match, P }: Pick<typeof import("ts-pattern"), "match" | "P"> = mapMangledModuleLazy("@ts-pattern/matcher", {
|
||||
match: filters.byCode("return new"),
|
||||
|
|
Loading…
Reference in a new issue