diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e945c0d0..013841274 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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 diff --git a/package.json b/package.json index 3bf1dadba..3b4add6df 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/packages/discord-types/enums/activity.ts b/packages/discord-types/enums/activity.ts new file mode 100644 index 000000000..513a65dee --- /dev/null +++ b/packages/discord-types/enums/activity.ts @@ -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 +} diff --git a/packages/discord-types/enums/index.ts b/packages/discord-types/enums/index.ts index 3d80895cd..a25ff3b5d 100644 --- a/packages/discord-types/enums/index.ts +++ b/packages/discord-types/enums/index.ts @@ -1,3 +1,4 @@ +export * from "./activity"; export * from "./channel"; export * from "./commands"; export * from "./messages"; diff --git a/packages/discord-types/src/common/Activity.d.ts b/packages/discord-types/src/common/Activity.d.ts index a4df551a0..acc8b8691 100644 --- a/packages/discord-types/src/common/Activity.d.ts +++ b/packages/discord-types/src/common/Activity.d.ts @@ -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; + assets?: ActivityAssets; + buttons?: string[]; metadata?: { button_urls?: Array; }; - timestamps?: ActivityTimestamp; platform?: string; } - -export const enum ActivityType { - PLAYING = 0, - STREAMING = 1, - LISTENING = 2, - WATCHING = 3, - COMPETING = 5 -} diff --git a/packages/discord-types/src/stores/MediaEngineStore.d.ts b/packages/discord-types/src/stores/MediaEngineStore.d.ts index 2452a6056..d44f5aa58 100644 --- a/packages/discord-types/src/stores/MediaEngineStore.d.ts +++ b/packages/discord-types/src/stores/MediaEngineStore.d.ts @@ -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; diff --git a/src/equicordplugins/betterActivities/types.ts b/src/equicordplugins/betterActivities/types.ts index e90524e62..de62b0ab3 100644 --- a/src/equicordplugins/betterActivities/types.ts +++ b/src/equicordplugins/betterActivities/types.ts @@ -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 -} diff --git a/src/equicordplugins/betterActivities/utils.tsx b/src/equicordplugins/betterActivities/utils.tsx index f1d0a9972..61de0479a 100644 --- a/src/equicordplugins/betterActivities/utils.tsx +++ b/src/equicordplugins/betterActivities/utils.tsx @@ -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; diff --git a/src/equicordplugins/gensokyoRadioRPC/index.tsx b/src/equicordplugins/gensokyoRadioRPC/index.tsx index 4a166406d..5aa943e93 100644 --- a/src/equicordplugins/gensokyoRadioRPC/index.tsx +++ b/src/equicordplugins/gensokyoRadioRPC/index.tsx @@ -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; @@ -96,7 +97,7 @@ export default definePlugin({ metadata: { button_urls: undefined }, type: ActivityType.LISTENING, - flags: ActivityFlag.INSTANCE, + flags: ActivityFlags.INSTANCE, }; } }); diff --git a/src/equicordplugins/gensokyoRadioRPC/types.ts b/src/equicordplugins/gensokyoRadioRPC/types.ts index 3fa0464b3..21b44194e 100644 --- a/src/equicordplugins/gensokyoRadioRPC/types.ts +++ b/src/equicordplugins/gensokyoRadioRPC/types.ts @@ -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; diff --git a/src/equicordplugins/imagePreview/index.ts b/src/equicordplugins/imagePreview/index.ts index 065d8a392..948dd870d 100644 --- a/src/equicordplugins/imagePreview/index.ts +++ b/src/equicordplugins/imagePreview/index.ts @@ -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); } diff --git a/src/equicordplugins/rpcEditor/ReplaceSettings.tsx b/src/equicordplugins/rpcEditor/ReplaceSettings.tsx index 701ef21e8..ab393408c 100644 --- a/src/equicordplugins/rpcEditor/ReplaceSettings.tsx +++ b/src/equicordplugins/rpcEditor/ReplaceSettings.tsx @@ -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() { <> IDs of currently running activities { - activities.length === 0 ? No running activities : activities.map(activity => { return activity.flags !== 48 ? {activity.name}: {activity.application_id} : null; /* hide spotify */ }) + activities.length === 0 + ? No running activities + : activities.map(activity => { + const isSpotify = (activity.flags & (ActivityFlags.SYNC | ActivityFlags.PLAY)) === (ActivityFlags.SYNC | ActivityFlags.PLAY); + return !isSpotify + ? {activity.name}: {activity.application_id} + : null; + }) } Available variables diff --git a/src/equicordplugins/rpcEditor/index.tsx b/src/equicordplugins/rpcEditor/index.tsx index b623caacf..e60d6627f 100644 --- a/src/equicordplugins/rpcEditor/index.tsx +++ b/src/equicordplugins/rpcEditor/index.tsx @@ -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; - name: string; - application_id: string; - metadata?: { - button_urls?: Array; - }; - 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); diff --git a/src/equicordplugins/statsfmPresence/index.tsx b/src/equicordplugins/statsfmPresence/index.tsx index fcb19ddc2..2d8b65782 100644 --- a/src/equicordplugins/statsfmPresence/index.tsx +++ b/src/equicordplugins/statsfmPresence/index.tsx @@ -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, }; } }); diff --git a/src/equicordplugins/tosuRPC/index.ts b/src/equicordplugins/tosuRPC/index.ts index 05d908b37..9e2a9faef 100644 --- a/src/equicordplugins/tosuRPC/index.ts +++ b/src/equicordplugins/tosuRPC/index.ts @@ -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; diff --git a/src/equicordplugins/tosuRPC/type.ts b/src/equicordplugins/tosuRPC/type.ts index 67c3e4957..7ded0748f 100644 --- a/src/equicordplugins/tosuRPC/type.ts +++ b/src/equicordplugins/tosuRPC/type.ts @@ -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; - name: string; - application_id: string; - metadata?: { - button_urls?: Array; - }; - 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, diff --git a/src/equicordplugins/vencordRPC/index.ts b/src/equicordplugins/vencordRPC/index.ts index ec8fb81c3..ba985418a 100644 --- a/src/equicordplugins/vencordRPC/index.ts +++ b/src/equicordplugins/vencordRPC/index.ts @@ -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 { 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; - name: string; - application_id: string; - metadata?: { - button_urls?: Array; - }; - type: ActivityType; - url?: string; - flags: number; -} - -const enum ActivityType { - PLAYING = 0, - STREAMING = 1, - LISTENING = 2, - WATCHING = 3, - COMPETING = 5 -} - const enum TimestampMode { NONE, NOW, diff --git a/src/equicordplugins/voiceButtons/index.tsx b/src/equicordplugins/voiceButtons/index.tsx new file mode 100644 index 000000000..dfe0bfca7 --- /dev/null +++ b/src/equicordplugins/voiceButtons/index.tsx @@ -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 ( +
+ {settings.store.showChatButton && } + {settings.store.showMuteButton && } + {settings.store.showDeafenButton && } +
+ ); + } +}); diff --git a/src/equicordplugins/voiceButtons/styles.css b/src/equicordplugins/voiceButtons/styles.css new file mode 100644 index 000000000..4dcda501c --- /dev/null +++ b/src/equicordplugins/voiceButtons/styles.css @@ -0,0 +1,3 @@ +div[class^='list_'][class*='collapsed_'] .click-to-chat-btn { + display: none; +} \ No newline at end of file diff --git a/src/equicordplugins/voiceButtons/utils.tsx b/src/equicordplugins/voiceButtons/utils.tsx new file mode 100644 index 000000000..63f5e41bf --- /dev/null +++ b/src/equicordplugins/voiceButtons/utils.tsx @@ -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 ( +
+ + {({ onMouseEnter, onMouseLeave }) => ( + + )} + +
+ ); +} + +export function UserMuteButton({ user }: { user: User; }) { + if (user.id === UserStore.getCurrentUser().id) return null; + return ( +
+ + {({ onMouseEnter, onMouseLeave }) => ( + + )} + +
+ ); +} + +function DeafenIcon({ user, ...props }) { + const isDeafened = + MediaEngineStore.isLocalMute(user.id) && + SoundboardStore.isLocalSoundboardMuted(user.id) && + MediaEngineStore.isLocalVideoDisabled(user.id); + + return ( + + + + {isDeafened && ( + + )} + + ); +} + +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 ( +
+ + {({ onMouseEnter, onMouseLeave }) => ( + + )} + +
+ ); +} diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index 57e5ae19e..f6fc9da93 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -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; -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; - name: string; - application_id: string; - metadata?: { - button_urls?: Array; - }; - 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, }; } }); diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index d35dad135..9a5a1cd98 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -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 { 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; - name: string; - application_id: string; - metadata?: { - button_urls?: Array; - }; - 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, diff --git a/src/plugins/lastfmRichPresence/index.tsx b/src/plugins/lastfmRichPresence/index.tsx index a6de8b188..3da7bf851 100644 --- a/src/plugins/lastfmRichPresence/index.tsx +++ b/src/plugins/lastfmRichPresence/index.tsx @@ -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; - name: string; - application_id: string; - status_display_type?: number; - metadata?: { - button_urls?: Array; - }; - 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, }; } }); diff --git a/src/plugins/messageLatency/index.tsx b/src/plugins/messageLatency/index.tsx index b23d4aadb..9ce3070a3 100644 --- a/src/plugins/messageLatency/index.tsx +++ b/src/plugins/messageLatency/index.tsx @@ -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) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index bfb5a4bc3..cec3b5004 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1148,6 +1148,10 @@ export const EquicordDevs = Object.freeze({ justjxke: { name: "justjxke", id: 852558183087472640n + }, + nicola02nb: { + name: "nicola02nb", + id: 257900031351193600n } } satisfies Record);