Merge branch 'dev'

This commit is contained in:
thororen1234 2025-09-22 09:14:57 -04:00
commit 5816c32c52
No known key found for this signature in database
25 changed files with 415 additions and 297 deletions

View file

@ -20,8 +20,8 @@ Before starting your plugin:
- Be in our discord
- Check existing pull requests to see if someone is already working on a similar plugin
- Check our [plugin requests tracker](https://discord.com/channels/1173279886065029291/1173334591302553631) to see if there is an existing request, or if the same idea has been rejected
- If there isn't an existing request, [open one](https://discord.com/channels/1173279886065029291/1173334591302553631) yourself.
- Check our [plugin requests tracker](https://discord.com/channels/1173279886065029291/1419347113745059961) to see if there is an existing request, or if the same idea has been rejected
- If there isn't an existing request, [open one](https://discord.com/channels/1173279886065029291/1419347113745059961) yourself.
and include that you'd like to work on this yourself. Then wait for feedback to see if the idea even has any chance of being accepted. Or maybe others have some ideas to improve it!
- Familarise yourself with our plugin rules below to ensure your plugin is not banned

View file

@ -1,7 +1,7 @@
{
"name": "equicord",
"private": "true",
"version": "1.12.13",
"version": "1.13.0",
"description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": {

View file

@ -0,0 +1,30 @@
export const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
CUSTOM_STATUS = 4,
COMPETING = 5,
HANG_STATUS = 6
}
export const enum ActivityFlags {
INSTANCE = 1 << 0,
JOIN = 1 << 1,
/** @deprecated */
SPECTATE = 1 << 2,
/** @deprecated */
JOIN_REQUEST = 1 << 3,
SYNC = 1 << 4,
PLAY = 1 << 5,
PARTY_PRIVACY_FRIENDS = 1 << 6,
PARTY_PRIVACY_VOICE_CHANNEL = 1 << 7,
EMBEDDED = 1 << 8,
CONTEXTLESS = 1 << 9
}
export const enum ActivityStatusDisplayType {
NAME = 0,
STATE = 1,
DETAILS = 2
}

View file

@ -1,3 +1,4 @@
export * from "./activity";
export * from "./channel";
export * from "./commands";
export * from "./messages";

View file

@ -1,45 +1,43 @@
import { CSSProperties, ImgHTMLAttributes, JSX } from "react";
import { Application } from "./Application";
import { User } from "./User";
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "../../enums";
export interface ActivityTimestamp {
start?: number;
end?: number;
export interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
export interface ActivityButton {
label: string;
url: string;
}
export interface Activity {
created_at: number;
id: string;
created_at?: number;
id?: string;
name: string;
type: number;
application_id: string;
type: ActivityType;
emoji?: {
animated: boolean;
id: string;
name: string;
};
state?: string;
flags?: number;
sync_id?: string;
state_url?: string;
details?: string;
application_id?: string;
assets?: {
large_text?: string;
large_image?: string;
small_text?: string;
small_image?: string;
details_url?: string;
url?: string;
flags: ActivityFlags;
status_display_type?: ActivityStatusDisplayType;
timestamps?: {
start?: number;
end?: number;
};
buttons?: Array<string>;
assets?: ActivityAssets;
buttons?: string[];
metadata?: {
button_urls?: Array<string>;
};
timestamps?: ActivityTimestamp;
platform?: string;
}
export const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}

View file

@ -3,6 +3,8 @@ import { FluxStore } from "..";
export class MediaEngineStore extends FluxStore {
isSelfMute(): boolean;
isSelfDeaf(): boolean;
isLocalMute(userId): boolean;
isLocalVideoDisabled(userId): boolean;
setAv1Enabled(AV1: boolean): void;
setH265Enabled(H265: boolean): void;

View file

@ -78,11 +78,3 @@ export interface ActivityViewProps {
application?: Application;
currentUser: User;
}
export const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}

View file

@ -44,7 +44,7 @@ export function getApplicationIcons(activities: Activity[], preferSmall = false)
for (const activity of applications) {
const { assets, application_id, platform, id } = activity;
if (!application_id && !platform && !id.startsWith("spotify:")) continue;
if (!application_id && !platform && !id?.startsWith("spotify:")) continue;
if (assets) {
const { small_image, small_text, large_image, large_text } = assets;

View file

@ -7,9 +7,10 @@
import { definePluginSettings } from "@api/Settings";
import { Devs, EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { ActivityFlags, ActivityType } from "@vencord/discord-types/enums";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
import { Activity, ActivityFlag, ActivityType } from "./types";
const Native = VencordNative.pluginHelpers.GensokyoRadioRPC as PluginNative<typeof import("./native")>;
@ -96,7 +97,7 @@ export default definePlugin({
metadata: { button_urls: undefined },
type: ActivityType.LISTENING,
flags: ActivityFlag.INSTANCE,
flags: ActivityFlags.INSTANCE,
};
}
});

View file

@ -36,15 +36,6 @@ export interface ActivityAssets {
small_text?: string;
}
export const enum ActivityType {
PLAYING = 0,
LISTENING = 2,
}
export const enum ActivityFlag {
INSTANCE = 1 << 0
}
export interface TrackData {
title: string;
album: string;

View file

@ -376,6 +376,24 @@ function updatePreviewPosition(mouseEvent: MouseEvent, element: HTMLElement) {
}
}
let previewCheckInterval;
function watchSourceElement(el: Element) {
if (previewCheckInterval) {
clearInterval(previewCheckInterval);
previewCheckInterval = null;
}
previewCheckInterval = setInterval(() => {
const node = el as HTMLElement;
if (!document.body.contains(node) || !node.offsetParent) {
deleteCurrentPreview();
clearInterval(previewCheckInterval);
previewCheckInterval = null;
}
}, 300);
}
function addHoverListener(element: Element, sticker: boolean = false) {
element.setAttribute("data-processed", "true");
@ -406,6 +424,7 @@ function addHoverListener(element: Element, sticker: boolean = false) {
hoverDelayTimeout = setTimeout(() => {
loadImagePreview(strippedURL, sticker);
watchSourceElement(element);
if (lastMouseEvent) {
updatePreviewPosition(lastMouseEvent, element as HTMLElement);
}

View file

@ -7,9 +7,11 @@
import { CheckedTextInput } from "@components/CheckedTextInput";
import { Margins } from "@utils/margins";
import { identity } from "@utils/misc";
import { Activity } from "@vencord/discord-types";
import { ActivityFlags, ActivityType } from "@vencord/discord-types/enums";
import { Card, Forms, PresenceStore, React, Select, SnowflakeUtils, Switch, TextInput, UserStore } from "@webpack/common";
import { Activity, ActivityType, AppIdSetting, makeEmptyAppId } from ".";
import { AppIdSetting, makeEmptyAppId } from ".";
interface SettingsProps {
appIds: AppIdSetting[];
@ -28,7 +30,14 @@ export function ReplaceTutorial() {
<>
<Forms.FormTitle tag="h3">IDs of currently running activities</Forms.FormTitle>
{
activities.length === 0 ? <Forms.FormText>No running activities</Forms.FormText> : activities.map(activity => { return activity.flags !== 48 ? <Forms.FormText>{activity.name}: {activity.application_id}</Forms.FormText> : null; /* hide spotify */ })
activities.length === 0
? <Forms.FormText>No running activities</Forms.FormText>
: activities.map(activity => {
const isSpotify = (activity.flags & (ActivityFlags.SYNC | ActivityFlags.PLAY)) === (ActivityFlags.SYNC | ActivityFlags.PLAY);
return !isSpotify
? <Forms.FormText>{activity.name}: {activity.application_id}</Forms.FormText>
: null;
})
}
<Forms.FormTitle tag="h3" className={Margins.top8}>Available variables</Forms.FormTitle>
<Forms.FormText>

View file

@ -9,11 +9,14 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { useForceUpdater } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { ActivityType } from "@vencord/discord-types/enums";
import { React } from "@webpack/common";
import { ReplaceSettings, ReplaceTutorial } from "./ReplaceSettings";
const APP_IDS_KEY = "ReplaceActivityType_appids";
export type AppIdSetting = {
disableAssets: boolean;
disableTimestamps: boolean;
@ -30,40 +33,6 @@ export type AppIdSetting = {
newStreamUrl: string;
};
export interface Activity {
state: string;
details: string;
timestamps?: {
start?: number;
end?: number;
};
url?: string;
assets: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: number;
flags: number;
}
interface ActivityAssets {
large_image: string;
large_text: string;
small_image: string;
small_text: string;
}
export const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}
export const makeEmptyAppId: () => AppIdSetting = () => ({
appId: "",
enabled: true,
@ -124,12 +93,12 @@ export default definePlugin({
if (text === "null") return "";
return text
.replaceAll(":name:", originalActivity.name)
.replaceAll(":details:", originalActivity.details)
.replaceAll(":state:", originalActivity.state)
.replaceAll(":large_image:", originalActivity.assets.large_image)
.replaceAll(":large_text:", originalActivity.assets.large_text)
.replaceAll(":small_image:", originalActivity.assets.small_image)
.replaceAll(":small_text:", originalActivity.assets.small_text);
.replaceAll(":details:", originalActivity.details ?? "")
.replaceAll(":state:", originalActivity.state ?? "")
.replaceAll(":large_image:", originalActivity.assets?.large_image ?? "")
.replaceAll(":large_text:", originalActivity.assets?.large_text ?? "")
.replaceAll(":small_image:", originalActivity.assets?.small_image ?? "")
.replaceAll(":small_text:", originalActivity.assets?.small_text ?? "");
},
patchActivity(activity: Activity) {
if (!activity) return;
@ -141,6 +110,7 @@ export default definePlugin({
if (app.newActivityType === ActivityType.STREAMING && app.newStreamUrl) activity.url = app.newStreamUrl;
if (app.newDetails) activity.details = this.parseField(app.newDetails, oldActivity);
if (app.newState) activity.state = this.parseField(app.newState, oldActivity);
if (!activity.assets) activity.assets = {};
if (app.newLargeImageText) activity.assets.large_text = this.parseField(app.newLargeImageText, oldActivity);
if (app.newLargeImageUrl) activity.assets.large_image = this.parseField(app.newLargeImageUrl, oldActivity);
if (app.newSmallImageText) activity.assets.small_text = this.parseField(app.newSmallImageText, oldActivity);

View file

@ -9,6 +9,7 @@ import { getUserSettingLazy } from "@api/UserSettings";
import { EquicordDevs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { ActivityFlags, ActivityType } from "@vencord/discord-types/enums";
import { findByPropsLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
@ -50,16 +51,6 @@ interface TrackData {
imageUrl?: string;
}
// only relevant enum values
const enum ActivityType {
PLAYING = 0,
LISTENING = 2,
}
const enum ActivityFlag {
INSTANCE = 1 << 0,
}
const enum NameFormat {
StatusName = "status-name",
ArtistFirst = "artist-first",
@ -69,8 +60,6 @@ const enum NameFormat {
albumsName = "albums"
}
interface Albums {
id: number;
image: string;
@ -373,7 +362,7 @@ export default definePlugin({
},
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
flags: ActivityFlag.INSTANCE,
flags: ActivityFlags.INSTANCE,
};
}
});

View file

@ -6,9 +6,11 @@
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { ActivityType } from "@vencord/discord-types/enums";
import { ApplicationAssetUtils, FluxDispatcher } from "@webpack/common";
import { Activity, ActivityType, BanchoStatusEnum, GameState, Modes, TosuApi, UserLoginStatus } from "./type";
import { BanchoStatusEnum, GameState, Modes, TosuApi, UserLoginStatus } from "./type";
const socketId = "tosu";
const OSU_APP_ID = "367827983903490050";
@ -64,6 +66,8 @@ async function onMessage(data: string) {
flags: 1 << 0,
};
activity.assets = {};
switch (profile.mode.number) {
case Modes.Osu:
activity.assets.small_image = OSU_STARDARD_SMALL_IMAGE;

View file

@ -4,40 +4,6 @@
* SPDX-License-Identifier: GPL-3.0-or-later
*/
export interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
export interface Activity {
state?: string;
details?: string;
timestamps?: {
start?: number;
end?: number;
};
assets: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: ActivityType;
url?: string;
flags: number;
}
export enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}
export enum BeatmapStatuses {
Unknown,
NotSubmitted = 1,

View file

@ -8,7 +8,8 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { isTruthy } from "@utils/guards";
import definePlugin, { OptionType } from "@utils/types";
import { Channel, FluxStore } from "@vencord/discord-types";
import { Activity, Channel, FluxStore } from "@vencord/discord-types";
import { ActivityType } from "@vencord/discord-types/enums";
import { findByPropsLazy, findStoreLazy } from "@webpack";
import { ApplicationAssetUtils, ChannelStore, FluxDispatcher, GuildStore, IconUtils, PresenceStore, RelationshipStore, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
@ -26,40 +27,6 @@ async function getApplicationAsset(key: string): Promise<string> {
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
}
interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
interface Activity {
state?: string;
details?: string;
timestamps?: {
start?: number;
end?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: ActivityType;
url?: string;
flags: number;
}
const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}
const enum TimestampMode {
NONE,
NOW,

View file

@ -0,0 +1,74 @@
/*
* 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 { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { User } from "@vencord/discord-types";
import { React } from "@webpack/common";
import { UserChatButton, UserDeafenButton, UserMuteButton } from "./utils";
export const settings = definePluginSettings({
showChatButton: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether you want to display the chat button",
restartNeeded: true,
},
showMuteButton: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether you want to display the mute button",
restartNeeded: true,
},
showDeafenButton: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether you want to display the deafen button",
restartNeeded: true,
},
muteSoundboard: {
type: OptionType.BOOLEAN,
default: true,
description: "Toggles their soundboard upon clicking deafen button.",
restartNeeded: false,
},
disableVideo: {
type: OptionType.BOOLEAN,
default: true,
description: "Toggles their video upon clicking deafen button.",
restartNeeded: false,
},
});
export default definePlugin({
name: "VoiceButtons",
description: "Quickly DM, mute, or deafen any user right from the voice-call panel.",
authors: [EquicordDevs.nicola02nb, EquicordDevs.omaw],
settings,
patches: [
{
find: "\"avatarContainerClass\",\"userNameClassName\"",
replacement: [
{
match: /:(\i)\.username.*?flipped\]:\i\}\),children:\[/,
replace: "$&$self.renderButtons($1),"
}
]
}
],
renderPing(user?: User) {
if (!user) return null;
return (
<div style={{ display: "flex", gap: "4px" }}>
{settings.store.showChatButton && <UserChatButton user={user} />}
{settings.store.showMuteButton && <UserMuteButton user={user} />}
{settings.store.showDeafenButton && <UserDeafenButton user={user} />}
</div>
);
}
});

View file

@ -0,0 +1,3 @@
div[class^='list_'][class*='collapsed_'] .click-to-chat-btn {
display: none;
}

View file

@ -0,0 +1,175 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./styles.css";
import { openPrivateChannel } from "@utils/discord";
import { User } from "@vencord/discord-types";
import { findStoreLazy } from "@webpack";
import { Button, MediaEngineStore, Tooltip, UserStore, VoiceActions } from "@webpack/common";
import { settings } from ".";
const SoundboardStore = findStoreLazy("SoundboardStore");
export function UserChatButton({ user }: { user: User; }) {
if (user.id === UserStore.getCurrentUser().id) return null;
return (
<div className="click-to-chat-container">
<Tooltip text={`Open chat with ${user.globalName} / @${user.username}`}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
size={Button.Sizes.MIN}
color={Button.Colors.TRANSPARENT}
look={Button.Looks.BLANK}
onClick={e => {
e.preventDefault();
e.stopPropagation();
openPrivateChannel({ recipientIds: [user.id] } as any);
}}
disabled={user.id === UserStore.getCurrentUser().id}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<svg
className="click-to-chat-button"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16"
fill="var(--channels-default)"
>
<path d="M12 22a10 10 0 1 0-8.45-4.64c.13.19.11.44-.04.61l-2.06 2.37A1 1 0 0 0 2.2 22H12Z"></path>
</svg>
</Button>
)}
</Tooltip>
</div>
);
}
export function UserMuteButton({ user }: { user: User; }) {
if (user.id === UserStore.getCurrentUser().id) return null;
return (
<div className="click-to-chat-container">
<Tooltip text={`${MediaEngineStore.isLocalMute(user.id) ? "Unmute" : "Mute"} ${user.globalName} / @${user.username}`}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
size={Button.Sizes.MIN}
color={Button.Colors.TRANSPARENT}
look={Button.Looks.BLANK}
onClick={e => {
e.preventDefault();
e.stopPropagation();
VoiceActions.toggleLocalMute(user.id);
}}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<svg
className="click-to-chat-button"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16"
fill={MediaEngineStore.isLocalMute(user.id) ? "red" : "var(--channels-default)"}
>
<path d="M10.78 17.22c-.25.25-.13.67.22.72V20H9a1 1 0 1 0 0 2h6a1 1 0 1 0 0-2h-2v-2.06A8 8 0 0 0 20 10a1 1 0 1 0-2 0 6 6 0 0 1-5.8 6 .52.52 0 0 0-.35.15l-1.07 1.07ZM15.4 4.6a.46.46 0 0 0 .1-.56 4 4 0 0 0-4.03-2c-.4.05-.47.57-.21.89A3.22 3.22 0 0 1 9.9 8l-1.16.43a.5.5 0 0 0-.3.3L8.01 9.9 8 9.94V10c0 .33.04.66.12.96.08.34.5.42.74.18L15.4 4.6ZM5.1 14.07c.17.26.53.3.75.08l.75-.75c.16-.16.19-.41.08-.62a5.99 5.99 0 0 1-.24-.51.62.62 0 0 0-.7-.35c-.23.05-.47.08-.7.08-.37 0-.7.34-.58.7.18.47.4.93.65 1.37Z"></path>
<path d="M9.2 3.86a1.21 1.21 0 0 1 0 2.28l-1.37.5a2 2 0 0 0-1.18 1.19l-.51 1.38a1.21 1.21 0 0 1-2.28 0l-.5-1.38a2 2 0 0 0-1.19-1.18L.79 6.14a1.21 1.21 0 0 1 0-2.28l1.38-.5a2 2 0 0 0 1.18-1.19L3.86.79a1.21 1.21 0 0 1 2.28 0l.5 1.38a2 2 0 0 0 1.19 1.18l1.38.51Z"></path>
<path
fillRule="evenodd"
d="M22.7 1.3a1 1 0 0 1 0 1.4l-20 20a1 1 0 0 1-1.4-1.4l20-20a1 1 0 0 1 1.4 0Z"
clipRule="evenodd"
></path>
</svg>
</Button>
)}
</Tooltip>
</div>
);
}
function DeafenIcon({ user, ...props }) {
const isDeafened =
MediaEngineStore.isLocalMute(user.id) &&
SoundboardStore.isLocalSoundboardMuted(user.id) &&
MediaEngineStore.isLocalVideoDisabled(user.id);
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
fill="none"
viewBox="0 0 24 24"
className={""}
{...props}
>
<path
fill={isDeafened ? "red" : "var(--channels-default)"}
fillRule="evenodd"
d="M21.76.83a5.02 5.02 0 0 1 .78 7.7 5 5 0 0 1-7.07 0 5.02 5.02 0 0 1 0-7.07 5 5 0 0 1 6.29-.63Zm-4.88 2.05a3 3 0 0 1 3.41-.59l-4 4a3 3 0 0 1 .59-3.41Zm4.83.83-4 4a3 3 0 0 0 4-4Z"
clipRule="evenodd"
className={""}
/>
<path
fill={isDeafened ? "red" : "var(--channels-default)"}
d="M12.38 1c.38.02.58.45.4.78-.15.3-.3.62-.4.95A.4.4 0 0 1 12 3a9 9 0 0 0-8.95 10h1.87a5 5 0 0 1 4.1 2.13l1.37 1.97a3.1 3.1 0 0 1-.17 3.78 2.85 2.85 0 0 1-3.55.74 11 11 0 0 1 5.71-20.61ZM22.22 11.22c.34-.18.76.02.77.4L23 12a11 11 0 0 1-5.67 9.62c-1.27.71-2.73.23-3.55-.74a3.1 3.1 0 0 1-.17-3.78l1.38-1.97a5 5 0 0 1 4.1-2.13h1.86c.03-.33.05-.66.05-1a.4.4 0 0 1 .27-.38c.33-.1.65-.25.95-.4Z"
className={""}
/>
{isDeafened && (
<line
x1="22"
y1="2"
x2="2"
y2="22"
stroke="var(--status-danger)"
strokeWidth="2"
/>
)}
</svg>
);
}
export function UserDeafenButton({ user }: { user: User; }) {
if (user.id === UserStore.getCurrentUser().id) return null;
const isDeafened =
MediaEngineStore.isLocalMute(user.id) &&
SoundboardStore.isLocalSoundboardMuted(user.id) &&
MediaEngineStore.isLocalVideoDisabled(user.id);
return (
<div className="click-to-deafen-container">
<Tooltip text={`${isDeafened ? "Undeafen" : "Deafen"} ${user.globalName} / @${user.username}`}>
{({ onMouseEnter, onMouseLeave }) => (
<Button
size={Button.Sizes.MIN}
color={Button.Colors.TRANSPARENT}
look={Button.Looks.BLANK}
onClick={e => {
e.preventDefault();
e.stopPropagation();
VoiceActions.toggleLocalMute(user.id);
if (settings.store.muteSoundboard) {
VoiceActions.toggleLocalSoundboardMute(user.id);
}
if (settings.store.disableVideo) {
VoiceActions.setDisableLocalVideo(
user.id,
MediaEngineStore.isLocalVideoDisabled(user.id) ? "ENABLED" : "DISABLED",
"default"
);
}
}}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
>
<DeafenIcon user={user} width={16} height={16} />
</Button>
)}
</Tooltip>
</div>
);
}

View file

@ -7,49 +7,12 @@
import { definePluginSettings } from "@api/Settings";
import { Devs, IS_MAC } from "@utils/constants";
import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types";
import { Activity, ActivityAssets, ActivityButton } from "@vencord/discord-types";
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "@vencord/discord-types/enums";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
const Native = VencordNative.pluginHelpers.AppleMusicRichPresence as PluginNative<typeof import("./native")>;
interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
interface ActivityButton {
label: string;
url: string;
}
interface Activity {
state?: string;
details?: string;
timestamps?: {
start?: number;
end?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: number;
flags: number;
}
const enum ActivityType {
PLAYING = 0,
LISTENING = 2,
}
const enum ActivityFlag {
INSTANCE = 1 << 0,
}
export interface TrackData {
name: string;
album?: string;
@ -90,6 +53,25 @@ const settings = definePluginSettings({
{ label: "Listening", value: ActivityType.LISTENING }
],
},
statusDisplayType: {
description: "Show the track / artist name in the member list",
type: OptionType.SELECT,
options: [
{
label: "Don't show (shows generic listening message)",
value: "off",
default: true
},
{
label: "Show artist name",
value: "artist"
},
{
label: "Show track name",
value: "track"
}
]
},
refreshInterval: {
type: OptionType.SLIDER,
description: "The interval between activity refreshes (seconds)",
@ -258,7 +240,12 @@ export default definePlugin({
metadata: !isRadio && buttons.length ? { button_urls: buttons.map(v => v.url) } : undefined,
type: settings.store.activityType,
flags: ActivityFlag.INSTANCE,
status_display_type: {
"off": ActivityStatusDisplayType.NAME,
"artist": ActivityStatusDisplayType.STATE,
"track": ActivityStatusDisplayType.DETAILS
}[settings.store.statusDisplayType],
flags: ActivityFlags.INSTANCE,
};
}
});

View file

@ -27,6 +27,8 @@ import { Margins } from "@utils/margins";
import { classes } from "@utils/misc";
import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types";
import { Activity } from "@vencord/discord-types";
import { ActivityType } from "@vencord/discord-types/enums";
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, React, UserStore } from "@webpack/common";
@ -39,41 +41,7 @@ async function getApplicationAsset(key: string): Promise<string> {
return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0];
}
interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
interface Activity {
state?: string;
details?: string;
timestamps?: {
start?: number;
end?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
metadata?: {
button_urls?: Array<string>;
};
type: ActivityType;
url?: string;
flags: number;
}
const enum ActivityType {
PLAYING = 0,
STREAMING = 1,
LISTENING = 2,
WATCHING = 3,
COMPETING = 5
}
const enum TimestampMode {
export const enum TimestampMode {
NONE,
NOW,
TIME,

View file

@ -22,40 +22,11 @@ import { Link } from "@components/Link";
import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { Activity, ActivityAssets, ActivityButton } from "@vencord/discord-types";
import { ActivityFlags, ActivityStatusDisplayType, ActivityType } from "@vencord/discord-types/enums";
import { findByPropsLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
interface ActivityAssets {
large_image?: string;
large_text?: string;
small_image?: string;
small_text?: string;
}
interface ActivityButton {
label: string;
url: string;
}
interface Activity {
state: string;
details?: string;
timestamps?: {
start?: number;
};
assets?: ActivityAssets;
buttons?: Array<string>;
name: string;
application_id: string;
status_display_type?: number;
metadata?: {
button_urls?: Array<string>;
};
type: number;
flags: number;
}
interface TrackData {
name: string;
album: string;
@ -64,16 +35,6 @@ interface TrackData {
imageUrl?: string;
}
// only relevant enum values
const enum ActivityType {
PLAYING = 0,
LISTENING = 2,
}
const enum ActivityFlag {
INSTANCE = 1 << 0,
}
const enum NameFormat {
StatusName = "status-name",
ArtistFirst = "artist-first",
@ -375,9 +336,9 @@ export default definePlugin({
details: trackData.name,
state: trackData.artist,
status_display_type: {
"off": 0,
"artist": 1,
"track": 2
"off": ActivityStatusDisplayType.NAME,
"artist": ActivityStatusDisplayType.STATE,
"track": ActivityStatusDisplayType.DETAILS
}[settings.store.statusDisplayType],
assets,
@ -387,7 +348,7 @@ export default definePlugin({
},
type: settings.store.useListeningStatus ? ActivityType.LISTENING : ActivityType.PLAYING,
flags: ActivityFlag.INSTANCE,
flags: ActivityFlags.INSTANCE,
};
}
});

View file

@ -11,7 +11,7 @@ import { isNonNullish } from "@utils/guards";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
import { findComponentByCodeLazy } from "@webpack";
import { SnowflakeUtils, Tooltip } from "@webpack/common";
import { AuthenticationStore, SnowflakeUtils, Tooltip } from "@webpack/common";
type FillValue = ("status-danger" | "status-warning" | "status-positive" | "text-muted");
type Fill = [FillValue, FillValue, FillValue];
@ -48,6 +48,11 @@ export default definePlugin({
type: OptionType.BOOLEAN,
description: "Show milliseconds",
default: false
},
ignoreSelf: {
type: OptionType.BOOLEAN,
description: "Don't add indicator to your own messages",
default: false
}
}),
@ -91,7 +96,7 @@ export default definePlugin({
},
latencyTooltipData(message: Message) {
const { latency, detectDiscordKotlin, showMillis } = this.settings.store;
const { latency, detectDiscordKotlin, showMillis, ignoreSelf } = this.settings.store;
const { id, nonce } = message;
// Message wasn't received through gateway
@ -100,6 +105,8 @@ export default definePlugin({
// Bots basically never send a nonce, and if someone does do it then it's usually not a snowflake
if (message.author.bot) return null;
if (ignoreSelf && message.author.id === AuthenticationStore.getId()) return null;
let isDiscordKotlin = false;
let delta = SnowflakeUtils.extractTimestamp(id) - SnowflakeUtils.extractTimestamp(nonce); // milliseconds
if (!showMillis) {

View file

@ -1148,6 +1148,10 @@ export const EquicordDevs = Object.freeze({
justjxke: {
name: "justjxke",
id: 852558183087472640n
},
nicola02nb: {
name: "nicola02nb",
id: 257900031351193600n
}
} satisfies Record<string, Dev>);