Compare commits

..

26 Commits

Author SHA1 Message Date
Vendicated
544edce9f9 bump to v1.5.8 2023-10-21 19:26:29 +02:00
Dea
e4485165d0 onePingPerDM: add settings (#1802)
Co-authored-by: V <vendicated@riseup.net>
Co-authored-by: Dea <dea-banana@riseup.net>
2023-10-21 18:42:37 +02:00
Macintosh II
fada76ec81 PlatformIndicators: make size same as other memberlist icons (#1789)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 18:41:56 +02:00
zImPatrick
f659c46031 FakeNitro: Add app icon customization (#1822)
Co-authored-by: V <vendicated@riseup.net>
2023-10-21 17:53:00 +02:00
Nuckyz
ae1dc4eab0 Make reporter ignore useless Discord errors (#1829) 2023-10-21 17:51:07 +02:00
Nuckyz
fe60a72b80 Fix NoPendingCount patch 2023-10-21 12:26:04 -03:00
Nuckyz
5a0b2ee3f5 Fix FakeNitro patch 2023-10-21 12:26:04 -03:00
Nuckyz
6c1b8b0d8a Fix MessageDecorationsAPI 2023-10-21 12:25:57 -03:00
Nuckyz
b2a1410a96 Remove useless Experiments patch 2023-10-21 12:03:54 -03:00
Nuckyz
c25c95eecd Fix IgnoreActivities making reporter angry 2023-10-21 12:00:09 -03:00
ioj4
d94418f42f fix: windows host update patching (#1820) 2023-10-19 11:14:40 +02:00
Vendicated
da1a8cdd67 web: Fix themes tab 2023-10-19 10:13:05 +02:00
Vendicated
5d7ede34d8 bump to v1.5.7 2023-10-19 01:23:59 +02:00
Vendicated
cd61354998 ContributorModal: Fix vertical overflow on multi word names 2023-10-19 00:22:49 +02:00
AutumnVN
a452945ac8 feat(plugin): NoTypingAnimation (#1680) 2023-10-19 00:14:14 +02:00
Marocco2
b577660800 feat(VcNarrator): add {{NICKNAME}} as placeholder (#1792) 2023-10-19 00:05:47 +02:00
AutumnVN
4f57c7eded betterNotes, userVoiceShow: fix padding issue (#1804) 2023-10-18 23:54:35 +02:00
Ryan Cao
e3e5da10a9 fix: Content Security Policy patching (#1814)
Co-authored-by: Vendicated <vendicated@riseup.net>
2023-10-18 23:44:29 +02:00
Vendicated
998ce72f3b ForceOwnerCrown: Remove log spam 2023-10-14 02:46:57 +02:00
AutumnVN
188d12d1a3 roleColorEverywhere: role group color in thread/forum (#1778) 2023-10-13 04:26:18 +02:00
AutumnVN
a522eab40d feat(plugin): NoMosaic (#1791) 2023-10-13 04:10:36 +02:00
AutumnVN
c2721f158f imageZoom: fix again (#1793)
Co-authored-by: V <vendicated@riseup.net>
2023-10-13 04:07:21 +02:00
Vendicated
61cd7b4d99 arrpc: refactor for use in vesktop 2023-10-13 03:49:58 +02:00
Marocco2
926af0d1cd feat(VcNarrator): add {{DISPLAY_NAME}} as placeholder (#1642)
Co-authored-by: V <vendicated@riseup.net>
2023-10-12 04:05:46 +02:00
sunnie
dcaf4aec97 fix moreUserTags (#1780) 2023-10-12 03:30:32 +02:00
Nuckyz
5a97adb435 Fix broken IgnoreActivities patch 2023-10-10 05:10:46 -03:00
26 changed files with 250 additions and 100 deletions

View File

@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
"version": "1.5.6",
"version": "1.5.8",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {

View File

@ -43,7 +43,7 @@ const nodeCommonOpts = {
format: "cjs",
platform: "node",
target: ["esnext"],
external: ["electron", ...commonOpts.external],
external: ["electron", "original-fs", ...commonOpts.external],
define: defines,
};

View File

@ -61,6 +61,13 @@ const report = {
otherErrors: [] as string[]
};
const IGNORED_DISCORD_ERRORS = [
"KeybindStore: Looking for callback action",
"Unable to process domain list delta: Client revision number is null",
"Downloading the full bad domains file",
/\[GatewaySocket\].{0,110}Cannot access '/
] as Array<string | RegExp>;
function toCodeBlock(s: string) {
s = s.replace(/```/g, "`\u200B`\u200B`");
return "```" + s + " ```";
@ -86,6 +93,8 @@ async function printReport() {
console.log(` - Error: ${toCodeBlock(p.error)}`);
});
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
console.log("## Discord Errors");
report.otherErrors.forEach(e => {
console.log(`- ${toCodeBlock(e)}`);

View File

@ -17,6 +17,7 @@
font-size: 20px;
height: 20px;
position: relative;
text-wrap: nowrap;
}
.vc-author-modal-name::before {

View File

@ -25,7 +25,7 @@ import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react";
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
import { findByPropsLazy, findLazy } from "@webpack";
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import { UserThemeHeader } from "main/themes";
import type { ComponentType, Ref, SyntheticEvent } from "react";
@ -41,7 +41,7 @@ type FileInput = ComponentType<{
}>;
const InviteActions = findByPropsLazy("resolveInvite");
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
const cl = classNameFactory("vc-settings-theme-");

View File

@ -62,6 +62,10 @@ if (IS_VESKTOP || !IS_VANILLA) {
} catch { }
const findHeader = (headers: Record<string, string[]>, headerName: Lowercase<string>) => {
return Object.keys(headers).find(h => h.toLowerCase() === headerName);
};
// Remove CSP
type PolicyResult = Record<string, string[]>;
@ -73,6 +77,7 @@ if (IS_VESKTOP || !IS_VANILLA) {
result[directiveKey] = directiveValue;
}
});
return result;
};
const stringifyPolicy = (policy: PolicyResult): string =>
@ -81,31 +86,39 @@ if (IS_VESKTOP || !IS_VANILLA) {
.map(directive => directive.flat().join(" "))
.join("; ");
function patchCsp(headers: Record<string, string[]>, header: string) {
if (header in headers) {
const patchCsp = (headers: Record<string, string[]>) => {
const header = findHeader(headers, "content-security-policy");
if (header) {
const csp = parsePolicy(headers[header][0]);
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
csp[directive] = ["*", "blob:", "data:", "vencord:", "'unsafe-inline'"];
csp[directive] ??= [];
csp[directive].push("*", "blob:", "data:", "vencord:", "'unsafe-inline'");
}
// TODO: Restrict this to only imported packages with fixed version.
// Perhaps auto generate with esbuild
csp["script-src"] ??= [];
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
headers[header] = [stringifyPolicy(csp)];
}
}
};
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
if (responseHeaders) {
if (resourceType === "mainFrame")
patchCsp(responseHeaders, "content-security-policy");
patchCsp(responseHeaders);
// Fix hosts that don't properly set the css content type, such as
// raw.githubusercontent.com
if (resourceType === "stylesheet")
responseHeaders["content-type"] = ["text/css"];
if (resourceType === "stylesheet") {
const header = findHeader(responseHeaders, "content-type");
if (header)
responseHeaders[header] = ["text/css"];
}
}
cb({ cancel: false, responseHeaders });
});

View File

@ -17,7 +17,7 @@
*/
import { app } from "electron";
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs";
import { basename, dirname, join } from "path";
function isNewer($new: string, old: string) {

View File

@ -27,7 +27,7 @@ export default definePlugin({
{
find: ".withMentionPrefix",
replacement: {
match: /(.roleDot.{10,50}{children:.{1,2})}\)/,
match: /(currentUserIsPremium:.{10,50}{children:.{1,2})}\)/,
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
}
}

View File

@ -58,6 +58,26 @@ export default definePlugin({
</>
),
async handleEvent(e: MessageEvent<any>) {
const data = JSON.parse(e.data);
const { activity } = data;
const assets = activity?.assets;
if (assets?.large_image) assets.large_image = await lookupAsset(activity.application_id, assets.large_image);
if (assets?.small_image) assets.small_image = await lookupAsset(activity.application_id, assets.small_image);
if (activity) {
const appId = activity.application_id;
apps[appId] ||= await lookupApp(appId);
const app = apps[appId];
activity.name ||= app.name;
}
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data });
},
async start() {
// ArmCord comes with its own arRPC implementation, so this plugin just confuses users
if ("armcord" in window) return;
@ -65,22 +85,7 @@ export default definePlugin({
if (ws) ws.close();
ws = new WebSocket("ws://127.0.0.1:1337"); // try to open WebSocket
ws.onmessage = async e => { // on message, set status to data
const data = JSON.parse(e.data);
if (data.activity?.assets?.large_image) data.activity.assets.large_image = await lookupAsset(data.activity.application_id, data.activity.assets.large_image);
if (data.activity?.assets?.small_image) data.activity.assets.small_image = await lookupAsset(data.activity.application_id, data.activity.assets.small_image);
if (data.activity) {
const appId = data.activity.application_id;
apps[appId] ||= await lookupApp(appId);
const app = apps[appId];
data.activity.name ||= app.name;
}
FluxDispatcher.dispatch({ type: "LOCAL_ACTIVITY_UPDATE", ...data });
};
ws.onmessage = this.handleEvent;
const connectionSuccessful = await new Promise(res => setTimeout(() => res(ws.readyState === WebSocket.OPEN), 1000)); // check if open after 1s
if (!connectionSuccessful) {

View File

@ -19,6 +19,9 @@
import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
export default definePlugin({
name: "BetterNotesBox",
@ -34,12 +37,20 @@ export default definePlugin({
match: /hideNote:.+?(?=[,}])/g,
replace: "hideNote:true",
}
}, {
},
{
find: "Messages.NOTE_PLACEHOLDER",
replacement: {
match: /\.NOTE_PLACEHOLDER,/,
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
}
},
{
find: ".Messages.NOTE}",
replacement: {
match: /(\i)\.hideNote\?null/,
replace: "$1.hideNote?$self.patchPadding($1)"
}
}
],
@ -56,5 +67,12 @@ export default definePlugin({
disabled: () => Settings.plugins.BetterNotesBox.hide,
default: false
}
},
patchPadding(e: any) {
if (!e.lastSection) return;
return (
<div className={UserPopoutSectionCssClasses.lastSection}></div>
);
}
});

View File

@ -77,15 +77,6 @@ export default definePlugin({
}
]
},
// Fix search history being disabled / broken with isStaff
{
find: 'get("disable_new_search")',
predicate: () => settings.store.enableIsStaff,
replacement: {
match: /(?<=showNewSearch"\);return)\s?!/,
replace: "!1&&!"
}
},
{
find: 'H1,title:"Experiments"',
replacement: {

View File

@ -295,7 +295,7 @@ export default definePlugin({
},
{
predicate: () => settings.store.transformStickers,
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
match: /renderAttachments=function\(\i\){var \i=this,(\i)=\i.attachments.+?;/,
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
}
]
@ -329,6 +329,20 @@ export default definePlugin({
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/,
replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)`
}
},
{
find: "canUsePremiumAppIcons:function",
replacement: {
match: /canUsePremiumAppIcons:function\(\i\){/,
replace: "$&return true;"
}
},
{
find: "location:\"AppIconHome\"",
replacement: {
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
replace: "true"
}
}
],

View File

@ -19,6 +19,7 @@
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { GuildStore } from "@webpack/common";
import { Channel, User } from "discord-types/general";
export default definePlugin({
name: "ForceOwnerCrown",
@ -34,25 +35,15 @@ export default definePlugin({
}
}
],
isGuildOwner(props) {
// Check if channel is a Group DM, if so return false
if (props?.channel?.type === 3) {
isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) {
if (!props?.user?.id) return false;
if (props.channel?.type === 3 /* GROUP_DM */)
return false;
}
// guild id is in props twice, fallback if the first is undefined
const guildId = props?.guildId ?? props?.channel?.guild_id;
const userId = props?.user?.id;
const guildId = props.guildId ?? props.channel?.guild_id;
const userId = props.user.id;
if (guildId && userId) {
const guild = GuildStore.getGuild(guildId);
if (guild) {
return guild.ownerId === userId;
}
console.error("[ForceOwnerCrown] failed to get guild", { guildId, guild, props });
} else {
console.error("[ForceOwnerCrown] no guildId or userId", { guildId, userId, props });
}
return false;
return GuildStore.getGuild(guildId)?.ownerId === userId;
},
});

View File

@ -106,7 +106,7 @@ export default definePlugin({
}
},
{
find: ".overlayBadge",
find: ".Messages.EMBEDDED_ACTIVITIES_DEVELOPER_SHELF_SUBTITLE",
replacement: [
{
match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/,

View File

@ -174,8 +174,8 @@ export default definePlugin({
find: "handleImageLoad=",
replacement: [
{
match: /(render=function\(\){.{1,500}limitResponsiveWidth.{1,600})onMouseEnter:/,
replace: "$1...$self.makeProps(this),onMouseEnter:"
match: /showThumbhashPlaceholder:/,
replace: "...$self.makeProps(this),$&"
},
{
@ -189,7 +189,6 @@ export default definePlugin({
}
]
},
{
find: ".carouselModal,",
replacement: {

View File

@ -53,8 +53,6 @@ interface TagSettings {
[k: string]: TagSetting;
}
const CLYDE_ID = "1081004946872352958";
// PermissionStore.computePermissions is not the same function and doesn't work here
const PermissionUtil = findByPropsLazy("computePermissions", "canEveryoneRole") as {
computePermissions({ ...args }): bigint;
@ -215,7 +213,7 @@ export default definePlugin({
},
// add HTML data attributes (for easier theming)
{
match: /children:\[(?=\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/,
match: /children:\[(?=\i\?null:\i,\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/,
replace: "'data-tag':$1.toLowerCase(),children:["
}
],
@ -230,10 +228,10 @@ export default definePlugin({
},
// in the member list
{
find: ".renderBot=function(){",
find: ".Messages.GUILD_OWNER,",
replacement: {
match: /\.BOT;return null!=(\i)&&.{0,10}\?(.{0,50})\.botTag,type:\i/,
replace: ".BOT;var type=$self.getTag({...this.props,origType:$1.bot?0:null,location:'not-chat'});return type!==null?$2.botTag,type"
match: /(?<type>\i)=\(null==.{0,50}\.BOT,null!=(?<user>\i)&&\i\.bot/,
replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }), typeof $<type> === 'number'"
}
},
// pass channel id down props to be used in profiles
@ -253,7 +251,7 @@ export default definePlugin({
},
// in profiles
{
find: ",botType:",
find: "showStreamerModeTooltip:",
replacement: {
match: /,botType:(\i\((\i)\)),/g,
replace: ",botType:$self.getTag({user:$2,channelId:arguments[0].moreTags_channelId,origType:$1,location:'not-chat'}),"
@ -341,15 +339,17 @@ export default definePlugin({
message, user, channelId, origType, location, channel
}: {
message?: Message,
user: User,
user: User & { isClyde(): boolean; },
channel?: Channel & { isForumPost(): boolean; },
channelId?: string;
origType?: number;
location: "chat" | "not-chat";
}): number | null {
if (!user)
return null;
if (location === "chat" && user.id === "1")
return Tag.Types.OFFICIAL;
if (user.id === CLYDE_ID)
if (user.isClyde())
return Tag.Types.AI;
let type = typeof origType === "number" ? origType : null;

View File

@ -0,0 +1,41 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { disableStyle, enableStyle } from "@api/Styles";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import style from "./styles.css?managed";
export default definePlugin({
name: "NoMosaic",
authors: [Devs.AutumnVN],
description: "Removes Discord new image mosaic",
tags: ["image", "mosaic", "media"],
patches: [{
find: "Media Mosaic",
replacement: [
{
match: /mediaLayoutType:\i\.\i\.MOSAIC/,
replace: 'mediaLayoutType:"RESPONSIVE"',
},
{
match: /\i===\i\.\i\.MOSAIC/,
replace: "true",
},
{
match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/,
replace: '"INVALID"',
},
],
}],
start() {
enableStyle(style);
},
stop() {
disableStyle(style);
}
});

View File

@ -0,0 +1,3 @@
[class^="nonMediaAttachmentsContainer-"] [class*="messageAttachment-"] {
position: relative;
}

View File

@ -84,8 +84,8 @@ export default definePlugin({
find: "showProgressBadge:",
predicate: () => settings.store.hidePremiumOffersCount,
replacement: {
match: /\(function\(\){return \i\.\i\.getUnacknowledgedOffers\(\i\)\.length}\)/,
replace: "(function(){return 0})"
match: /=\i\.unviewedTrialCount\+\i\.unviewedDiscountCount/,
replace: "=0"
}
}
],

View File

@ -0,0 +1,21 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2023 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
export default definePlugin({
name: "NoTypingAnimation",
authors: [Devs.AutumnVN],
description: "Disables the CPU-intensive typing dots animation",
patches: [{
find: "dotCycle",
replacement: {
match: /document.hasFocus\(\)/,
replace: "false"
}
}]
});

View File

@ -4,20 +4,44 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { ChannelStore, ReadStateStore } from "@webpack/common";
import { Message } from "discord-types/general";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, ReadStateStore, UserStore } from "@webpack/common";
import { MessageJSON } from "discord-types/general";
const enum ChannelType {
DM = 1,
GROUP_DM = 3
}
const settings = definePluginSettings({
channelToAffect: {
type: OptionType.SELECT,
description: "Select the type of DM for the plugin to affect",
options: [
{ label: "Both", value: "both_dms", default: true },
{ label: "User DMs", value: "user_dm" },
{ label: "Group DMs", value: "group_dm" },
]
},
allowMentions: {
type: OptionType.BOOLEAN,
description: "Receive audio pings for @mentions",
default: false,
},
allowEveryone: {
type: OptionType.BOOLEAN,
description: "Receive audio pings for @everyone and @here in group DMs",
default: false,
},
});
export default definePlugin({
name: "OnePingPerDM",
description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit",
authors: [Devs.ProffDea],
settings,
patches: [{
find: ".getDesktopType()===",
replacement: [{
@ -29,11 +53,19 @@ export default definePlugin({
replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1"
}]
}],
isPrivateChannelRead(message: Message) {
isPrivateChannelRead(message: MessageJSON) {
const channelType = ChannelStore.getChannel(message.channel_id)?.type;
if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) {
return false;
}
if (
(channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") ||
(channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") ||
(settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) ||
(settings.store.allowEveryone && message.mention_everyone)
) {
return true;
}
return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id;
},
});

View File

@ -30,13 +30,13 @@ import { User } from "discord-types/general";
const SessionsStore = findStoreLazy("SessionsStore");
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => (
<Tooltip text={tooltip} >
{(tooltipProps: any) => (
<svg
{...tooltipProps}
height={opts?.height ?? 20}
width={opts?.width ?? 20}
height={(opts?.height ?? 20) - (small ? 3 : 0)}
width={(opts?.width ?? 20) - (small ? 3 : 0)}
viewBox={opts?.viewBox ?? "0 0 24 24"}
fill={color}
>
@ -57,16 +57,16 @@ type Platform = keyof typeof Icons;
const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE");
const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => {
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
const tooltip = platform[0].toUpperCase() + platform.slice(1);
const Icon = Icons[platform] ?? Icons.desktop;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} small={small} />;
};
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => {
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, small = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; small?: boolean; }) => {
if (!user || user.bot) return null;
if (user.id === UserStore.getCurrentUser().id) {
@ -99,6 +99,7 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: {
key={platform}
platform={platform as Platform}
status={status}
small={small}
/>
));
@ -137,7 +138,7 @@ const indicatorLocations = {
description: "In the member list",
onEnable: () => addDecorator("platform-indicator", props =>
<ErrorBoundary noop>
<PlatformIndicator user={props.user} />
<PlatformIndicator user={props.user} small={true} />
</ErrorBoundary>
),
onDisable: () => removeDecorator("platform-indicator")

View File

@ -44,7 +44,7 @@ const settings = definePluginSettings({
export default definePlugin({
name: "RoleColorEverywhere",
authors: [Devs.KingFish, Devs.lewisakura],
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN],
description: "Adds the top role color anywhere possible",
patches: [
// Chat Mentions
@ -78,6 +78,10 @@ export default definePlugin({
match: /(memo\(\(function\((\i)\).{300,500}CHANNEL_MEMBERS_A11Y_LABEL.{100,200}roleIcon.{5,20}null,).," \u2014 ",.\]/,
replace: "$1$self.roleGroupColor($2)]"
},
{
match: /children:\[.," \u2014 ",.\]/,
replace: "children:[$self.roleGroupColor(arguments[0])]"
},
],
predicate: () => settings.store.memberList,
},
@ -105,7 +109,7 @@ export default definePlugin({
return colorString && parseInt(colorString.slice(1), 16);
},
roleGroupColor({ id, count, title, guildId }: { id: string; count: number; title: string; guildId: string; }) {
roleGroupColor({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) {
const guild = GuildStore.getGuild(guildId);
const role = guild?.roles[id];
@ -113,7 +117,7 @@ export default definePlugin({
color: role?.colorString,
fontWeight: "unset",
letterSpacing: ".05em"
}}>{title} &mdash; {count}</span>;
}}>{title ?? label} &mdash; {count}</span>;
},
getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) {

View File

@ -21,6 +21,7 @@
margin-bottom: 0 !important;
}
.vc-uvs-popout-margin > [class^="section"] {
margin-top: -12px;
.vc-uvs-popout-margin-self>[class^="section"] {
padding-top: 0;
padding-bottom: 12px;
}

View File

@ -20,14 +20,13 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { findStoreLazy } from "@webpack";
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
import { User } from "discord-types/general";
import { VoiceChannelSection } from "./components/VoiceChannelSection";
const VoiceStateStore = findStoreLazy("VoiceStateStore");
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
const settings = definePluginSettings({
showInUserProfileModal: {
@ -88,7 +87,7 @@ export default definePlugin({
patchPopout: ({ user }: UserProps) => {
const isSelfUser = user.id === UserStore.getCurrentUser().id;
return (
<div className={isSelfUser ? `vc-uvs-popout-margin ${UserPopoutSectionCssClasses.lastSection}` : ""}>
<div className={isSelfUser ? "vc-uvs-popout-margin-self" : ""}>
<VoiceChannelField user={user} />
</div>
);

View File

@ -24,7 +24,7 @@ import { Margins } from "@utils/margins";
import { wordsToTitle } from "@utils/text";
import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Button, ChannelStore, Forms, SelectedChannelStore, useMemo, UserStore } from "@webpack/common";
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
interface VoiceState {
userId: string;
@ -70,10 +70,12 @@ function clean(str: string) {
.trim();
}
function formatText(str: string, user: string, channel: string) {
function formatText(str: string, user: string, channel: string, displayName: string, nickname: string) {
return str
.replaceAll("{{USER}}", clean(user) || (user ? "Someone" : ""))
.replaceAll("{{CHANNEL}}", clean(channel) || "channel");
.replaceAll("{{CHANNEL}}", clean(channel) || "channel")
.replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : ""))
.replaceAll("{{NICKNAME}}", clean(nickname) || (nickname ? "Someone" : ""));
}
/*
@ -143,8 +145,10 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId,
function playSample(tempSettings: any, type: string) {
const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings);
const currentUser = UserStore.getCurrentUser();
const myGuildId = SelectedGuildStore.getGuildId();
speak(formatText(settings[type + "Message"], UserStore.getCurrentUser().username, "general"), settings);
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), settings);
}
export default definePlugin({
@ -154,6 +158,7 @@ export default definePlugin({
flux: {
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
const myGuildId = SelectedGuildStore.getGuildId();
const myChanId = SelectedChannelStore.getVoiceChannelId();
const myId = UserStore.getCurrentUser().id;
@ -172,9 +177,11 @@ export default definePlugin({
const template = Settings.plugins.VcNarrator[type + "Message"];
const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username;
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
const channel = ChannelStore.getChannel(id).name;
speak(formatText(template, user, channel));
speak(formatText(template, user, channel, displayName, nickname));
// updateStatuses(type, state, isMe);
}
@ -186,7 +193,7 @@ export default definePlugin({
if (!s) return;
const event = s.mute || s.selfMute ? "unmute" : "mute";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name));
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
},
AUDIO_TOGGLE_SELF_DEAF() {
@ -195,7 +202,7 @@ export default definePlugin({
if (!s) return;
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name));
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
}
},
@ -312,8 +319,8 @@ export default definePlugin({
You can customise the spoken messages below. You can disable specific messages by setting them to nothing
</Forms.FormText>
<Forms.FormText>
The special placeholders <code>{"{{USER}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
will be replaced with the user's name (nothing if it's yourself) and the channel's name respectively
The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code>, <code>{"{{NICKNAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
will be replaced with the user's name (nothing if it's yourself), the user's display name, the user's nickname on current server and the channel's name respectively
</Forms.FormText>
{hasEnglishVoices && (
<>