add universalMention (#471)
* hopefully the first and last commit * this took too long to make smh * Fix Patches * Update find * CustomRPC: add detail/state and image URL fields (#3758) Co-authored-by: Vendicated <vendicated@riseup.net> * fix missing Toolbox * fix Decor & NoTrack (#3752) Co-authored-by: Vendicated <vendicated@riseup.net> * bump to v1.13.5 * DM recipients fix and DM filtering * hopefully the first and last commit * this took too long to make smh * Fix Patches * Update find * DM recipients fix and DM filtering * probably fine * 🍤 * Fix Memory Leak * Experiments: Fix Patches --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> Co-authored-by: Vendicated <vendicated@riseup.net> Co-authored-by: hich4t <hich4t@enchoral.me>
This commit is contained in:
parent
ba7b42ad5f
commit
94f35e7e5e
7 changed files with 126 additions and 21 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "equicord",
|
||||
"private": "true",
|
||||
"version": "1.13.4",
|
||||
"version": "1.13.5",
|
||||
"description": "The other cutest Discord client mod",
|
||||
"homepage": "https://github.com/Equicord/Equicord#readme",
|
||||
"bugs": {
|
||||
|
|
@ -121,4 +121,4 @@
|
|||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,8 +3,10 @@ import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "../../en
|
|||
export interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
large_url?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
small_url?: string;
|
||||
}
|
||||
|
||||
export interface ActivityButton {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import { Devs } from "@utils/constants";
|
|||
import definePlugin from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Menu, Popout, useRef, useState } from "@webpack/common";
|
||||
import type { ReactNode } from "react";
|
||||
import type { PropsWithChildren, ReactNode } from "react";
|
||||
|
||||
const HeaderBarIcon = findComponentByCodeLazy(".HEADER_BAR_BADGE_TOP:", '.iconBadge,"top"');
|
||||
|
||||
|
|
@ -154,16 +154,20 @@ export default definePlugin({
|
|||
{
|
||||
find: '?"BACK_FORWARD_NAVIGATION":',
|
||||
replacement: {
|
||||
// TODO: (?:\.button) is for stable compat and should be removed soon:tm:
|
||||
match: /focusSectionProps:"HELP".{0,20},className:(\i(?:\.button)?)\}\),/,
|
||||
replace: "$& $self.renderVencordPopoutButton($1),"
|
||||
match: /(?<=trailing:.{0,50})\i\.Fragment,\{(?=.+?className:(\i))/,
|
||||
replace: "$self.TrailingWrapper,{className:$1,"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
renderVencordPopoutButton: (buttonClass: string) => (
|
||||
<ErrorBoundary noop>
|
||||
<VencordPopoutButton buttonClass={buttonClass} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
TrailingWrapper({ children, className }: PropsWithChildren<{ className: string; }>) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<ErrorBoundary noop>
|
||||
<VencordPopoutButton buttonClass={className} />
|
||||
</ErrorBoundary>
|
||||
</>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
65
src/equicordplugins/universalMention/index.tsx
Normal file
65
src/equicordplugins/universalMention/index.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Paragraph } from "@components/Paragraph";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { User } from "@vencord/discord-types";
|
||||
import { ChannelStore, UserStore } from "@webpack/common";
|
||||
|
||||
const settings = definePluginSettings({
|
||||
globalMention: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Mention users from any server, not just the current one",
|
||||
default: false,
|
||||
restartNeeded: true
|
||||
},
|
||||
onlyDMUsers: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Only show users you've had DMs with",
|
||||
default: false,
|
||||
restartNeeded: true
|
||||
}
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "UniversalMention",
|
||||
authors: [EquicordDevs.justjxke],
|
||||
description: "Mention any user, regardless of channel access.",
|
||||
settings,
|
||||
settingsAboutComponent: () => (
|
||||
<Paragraph className="plugin-warning">
|
||||
Using Global Mention can cause performance issues and show an absurd amount of users in the autocomplete.
|
||||
</Paragraph>
|
||||
),
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: ",queryMentionResults(",
|
||||
replacement: [
|
||||
{
|
||||
match: /(filter:\i).{0,75}context:\i\}\)(?=,allowSnowflake)/,
|
||||
replace: "$1=>true",
|
||||
},
|
||||
{
|
||||
match: /(?<=\i=)\i\.\i\.getMembers\(.{0,25}\)\.filter\(\i\)/g,
|
||||
replace: "$self.useFilter()",
|
||||
predicate: () => settings.store.globalMention || settings.store.onlyDMUsers,
|
||||
},
|
||||
{
|
||||
match: /(?<=\i=)\i\.recipients\.map\(.{0,100}:null\}\}\)/,
|
||||
replace: "$self.useFilter(true)",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
useFilter(map: boolean = false) {
|
||||
const foundUsers = Object.values(UserStore.getUsers()) as User[];
|
||||
const users = settings.store.onlyDMUsers ? foundUsers.filter(user => ChannelStore.getDMFromUserId(user.id)) : foundUsers;
|
||||
return map ? users.map(user => ({ userId: user.id, nick: null })) : users;
|
||||
}
|
||||
});
|
||||
|
|
@ -73,6 +73,11 @@ function isNumberValid(value: number) {
|
|||
return true;
|
||||
}
|
||||
|
||||
function isUrlValid(value: string) {
|
||||
if (value && !/^https?:\/\/.+/.test(value)) return "Must be a valid URL.";
|
||||
return true;
|
||||
}
|
||||
|
||||
function isImageKeyValid(value: string) {
|
||||
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//.test(value)) return "Don't use a Discord link. Use an Imgur image link instead.";
|
||||
if (/https?:\/\/(?!i\.)?imgur\.com\//.test(value)) return "Imgur link must be a direct link to the image (e.g. https://i.imgur.com/...). Right click the image and click 'Copy image address'";
|
||||
|
|
@ -180,8 +185,15 @@ export function RPCSettings() {
|
|||
{ settingsKey: "appName", label: "Application Name", isValid: makeValidator(128, true) },
|
||||
]} />
|
||||
|
||||
<SingleSetting settingsKey="details" label="Detail (line 1)" isValid={maxLength128} />
|
||||
<SingleSetting settingsKey="state" label="State (line 2)" isValid={maxLength128} />
|
||||
<PairSetting data={[
|
||||
{ settingsKey: "details", label: "Detail (line 1)", isValid: maxLength128 },
|
||||
{ settingsKey: "detailsURL", label: "Detail URL", isValid: isUrlValid },
|
||||
]} />
|
||||
|
||||
<PairSetting data={[
|
||||
{ settingsKey: "state", label: "State (line 2)", isValid: maxLength128 },
|
||||
{ settingsKey: "stateURL", label: "State URL", isValid: isUrlValid },
|
||||
]} />
|
||||
|
||||
<SingleSetting
|
||||
settingsKey="streamLink"
|
||||
|
|
@ -195,13 +207,15 @@ export function RPCSettings() {
|
|||
settingsKey: "partySize",
|
||||
label: "Party Size",
|
||||
transform: parseNumber,
|
||||
isValid: isNumberValid
|
||||
isValid: isNumberValid,
|
||||
disabled: s.type !== ActivityType.PLAYING,
|
||||
},
|
||||
{
|
||||
settingsKey: "partyMaxSize",
|
||||
label: "Maximum Party Size",
|
||||
transform: parseNumber,
|
||||
isValid: isNumberValid
|
||||
isValid: isNumberValid,
|
||||
disabled: s.type !== ActivityType.PLAYING,
|
||||
},
|
||||
]} />
|
||||
|
||||
|
|
@ -211,21 +225,23 @@ export function RPCSettings() {
|
|||
{ settingsKey: "imageBig", label: "Large Image URL/Key", isValid: isImageKeyValid },
|
||||
{ settingsKey: "imageBigTooltip", label: "Large Image Text", isValid: maxLength128 },
|
||||
]} />
|
||||
<SingleSetting settingsKey="imageBigURL" label="Large Image clickable URL" isValid={isUrlValid} />
|
||||
|
||||
<PairSetting data={[
|
||||
{ settingsKey: "imageSmall", label: "Small Image URL/Key", isValid: isImageKeyValid },
|
||||
{ settingsKey: "imageSmallTooltip", label: "Small Image Text", isValid: maxLength128 },
|
||||
]} />
|
||||
<SingleSetting settingsKey="imageSmallURL" label="Small Image clickable URL" isValid={isUrlValid} />
|
||||
|
||||
<Divider />
|
||||
|
||||
<PairSetting data={[
|
||||
{ settingsKey: "buttonOneText", label: "Button1 Text", isValid: makeValidator(31) },
|
||||
{ settingsKey: "buttonOneURL", label: "Button1 URL" },
|
||||
{ settingsKey: "buttonOneURL", label: "Button1 URL", isValid: isUrlValid },
|
||||
]} />
|
||||
<PairSetting data={[
|
||||
{ settingsKey: "buttonTwoText", label: "Button2 Text", isValid: makeValidator(31) },
|
||||
{ settingsKey: "buttonTwoURL", label: "Button2 URL" },
|
||||
{ settingsKey: "buttonTwoURL", label: "Button2 URL", isValid: isUrlValid },
|
||||
]} />
|
||||
|
||||
<Divider />
|
||||
|
|
|
|||
|
|
@ -62,15 +62,19 @@ export const settings = definePluginSettings({
|
|||
appID?: string;
|
||||
appName?: string;
|
||||
details?: string;
|
||||
detailsURL?: string;
|
||||
state?: string;
|
||||
stateURL?: string;
|
||||
type?: ActivityType;
|
||||
streamLink?: string;
|
||||
timestampMode?: TimestampMode;
|
||||
startTime?: number;
|
||||
endTime?: number;
|
||||
imageBig?: string;
|
||||
imageBigURL?: string;
|
||||
imageBigTooltip?: string;
|
||||
imageSmall?: string;
|
||||
imageSmallURL?: string;
|
||||
imageSmallTooltip?: string;
|
||||
buttonOneText?: string;
|
||||
buttonOneURL?: string;
|
||||
|
|
@ -85,14 +89,18 @@ async function createActivity(): Promise<Activity | undefined> {
|
|||
appID,
|
||||
appName,
|
||||
details,
|
||||
detailsURL,
|
||||
state,
|
||||
stateURL,
|
||||
type,
|
||||
streamLink,
|
||||
startTime,
|
||||
endTime,
|
||||
imageBig,
|
||||
imageBigURL,
|
||||
imageBigTooltip,
|
||||
imageSmall,
|
||||
imageSmallURL,
|
||||
imageSmallTooltip,
|
||||
buttonOneText,
|
||||
buttonOneURL,
|
||||
|
|
@ -139,6 +147,14 @@ async function createActivity(): Promise<Activity | undefined> {
|
|||
break;
|
||||
}
|
||||
|
||||
if (detailsURL) {
|
||||
activity.details_url = detailsURL;
|
||||
}
|
||||
|
||||
if (stateURL) {
|
||||
activity.state_url = stateURL;
|
||||
}
|
||||
|
||||
if (buttonOneText) {
|
||||
activity.buttons = [
|
||||
buttonOneText,
|
||||
|
|
@ -156,7 +172,8 @@ async function createActivity(): Promise<Activity | undefined> {
|
|||
if (imageBig) {
|
||||
activity.assets = {
|
||||
large_image: await getApplicationAsset(imageBig),
|
||||
large_text: imageBigTooltip || undefined
|
||||
large_text: imageBigTooltip || undefined,
|
||||
large_url: imageBigURL || undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +181,8 @@ async function createActivity(): Promise<Activity | undefined> {
|
|||
activity.assets = {
|
||||
...activity.assets,
|
||||
small_image: await getApplicationAsset(imageSmall),
|
||||
small_text: imageSmallTooltip || undefined
|
||||
small_text: imageSmallTooltip || undefined,
|
||||
small_url: imageSmallURL || undefined
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ export default definePlugin({
|
|||
},
|
||||
// Enable experiment embed on sent experiment links
|
||||
{
|
||||
find: "dev://experiment/",
|
||||
find: ".experimentOverride,children",
|
||||
replacement: [
|
||||
{
|
||||
match: /\i\.isStaff\(\)/,
|
||||
|
|
@ -104,7 +104,7 @@ export default definePlugin({
|
|||
},
|
||||
// Fix some tricky experiments name causing a client crash
|
||||
{
|
||||
match: /.getExperimentBucketName.+?if\(null==(\i)\|\|null==\i(?=\)return null;)/,
|
||||
match: /if\(null==(\i)\|\|null==\i(?=\)return null;)/,
|
||||
replace: "$&||({})[$1]!=null"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue