Merge branch 'dev'
This commit is contained in:
commit
5816c32c52
25 changed files with 415 additions and 297 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
30
packages/discord-types/enums/activity.ts
Normal file
30
packages/discord-types/enums/activity.ts
Normal 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
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
export * from "./activity";
|
||||
export * from "./channel";
|
||||
export * from "./commands";
|
||||
export * from "./messages";
|
||||
|
|
|
|||
52
packages/discord-types/src/common/Activity.d.ts
vendored
52
packages/discord-types/src/common/Activity.d.ts
vendored
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
74
src/equicordplugins/voiceButtons/index.tsx
Normal file
74
src/equicordplugins/voiceButtons/index.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
});
|
||||
3
src/equicordplugins/voiceButtons/styles.css
Normal file
3
src/equicordplugins/voiceButtons/styles.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
div[class^='list_'][class*='collapsed_'] .click-to-chat-btn {
|
||||
display: none;
|
||||
}
|
||||
175
src/equicordplugins/voiceButtons/utils.tsx
Normal file
175
src/equicordplugins/voiceButtons/utils.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1148,6 +1148,10 @@ export const EquicordDevs = Object.freeze({
|
|||
justjxke: {
|
||||
name: "justjxke",
|
||||
id: 852558183087472640n
|
||||
},
|
||||
nicola02nb: {
|
||||
name: "nicola02nb",
|
||||
id: 257900031351193600n
|
||||
}
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue