feat(UserAreaAPI): ability to add buttons to user-area (#560)
* feat(UserAreaAPI): add UserArea button API * fix supporthelper plugin card * lint * fix and revert * fix horror
This commit is contained in:
parent
7202091567
commit
54718f88f6
12 changed files with 238 additions and 125 deletions
|
|
@ -42,6 +42,7 @@ export { Plugins as plugins };
|
|||
import { traceFunction } from "../debug/Tracer";
|
||||
import { addAudioProcessor, removeAudioProcessor } from "./AudioPlayer";
|
||||
import { addChannelToolbarButton, addHeaderBarButton, removeChannelToolbarButton, removeHeaderBarButton } from "./HeaderBar";
|
||||
import { addUserAreaButton, removeUserAreaButton } from "./UserArea";
|
||||
|
||||
const logger = new Logger("PluginManager", "#a6d189");
|
||||
|
||||
|
|
@ -195,7 +196,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p:
|
|||
onBeforeMessageEdit, onBeforeMessageSend, onMessageClick,
|
||||
renderChatBarButton, chatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton, messagePopoverButton,
|
||||
// Custom
|
||||
renderNicknameIcon, headerBarButton, audioProcessor
|
||||
renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton
|
||||
} = p;
|
||||
|
||||
if (p.start) {
|
||||
|
|
@ -265,6 +266,7 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p:
|
|||
}
|
||||
}
|
||||
if (audioProcessor) addAudioProcessor(name, audioProcessor);
|
||||
if (userAreaButton) addUserAreaButton(name, userAreaButton.render, userAreaButton.priority);
|
||||
|
||||
return true;
|
||||
}, p => `startPlugin ${p.name}`);
|
||||
|
|
@ -275,7 +277,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu
|
|||
onBeforeMessageEdit, onBeforeMessageSend, onMessageClick,
|
||||
renderChatBarButton, chatBarButton, renderMemberListDecorator, renderMessageAccessory, renderMessageDecoration, renderMessagePopoverButton, messagePopoverButton,
|
||||
// Custom
|
||||
renderNicknameIcon, headerBarButton, audioProcessor
|
||||
renderNicknameIcon, headerBarButton, audioProcessor, userAreaButton
|
||||
} = p;
|
||||
|
||||
if (p.stop) {
|
||||
|
|
@ -339,6 +341,7 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu
|
|||
}
|
||||
}
|
||||
if (audioProcessor) removeAudioProcessor(name);
|
||||
if (userAreaButton) removeUserAreaButton(name);
|
||||
|
||||
return true;
|
||||
}, p => `stopPlugin ${p.name}`);
|
||||
|
|
@ -392,6 +395,7 @@ export const initPluginManager = onlyOnce(function init() {
|
|||
if (p.renderNicknameIcon) neededApiPlugins.add("NicknameIconsAPI");
|
||||
if (p.headerBarButton) neededApiPlugins.add("HeaderBarAPI");
|
||||
if (p.audioProcessor) neededApiPlugins.add("AudioPlayerAPI");
|
||||
if (p.userAreaButton) neededApiPlugins.add("UserAreaAPI");
|
||||
|
||||
for (const key of pluginKeysToBind) {
|
||||
p[key] &&= p[key].bind(p) as any;
|
||||
|
|
|
|||
78
src/api/UserArea.tsx
Normal file
78
src/api/UserArea.tsx
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import type { ComponentType, MouseEventHandler, ReactNode } from "react";
|
||||
|
||||
const PanelButton = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON") as ComponentType<UserAreaButtonProps>;
|
||||
|
||||
export interface UserAreaButtonProps {
|
||||
icon: ReactNode;
|
||||
tooltipText?: ReactNode;
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
onContextMenu?: MouseEventHandler<HTMLDivElement>;
|
||||
className?: string;
|
||||
role?: string;
|
||||
"aria-label"?: string;
|
||||
"aria-checked"?: boolean;
|
||||
disabled?: boolean;
|
||||
plated?: boolean;
|
||||
redGlow?: boolean;
|
||||
orangeGlow?: boolean;
|
||||
}
|
||||
|
||||
export interface UserAreaRenderProps {
|
||||
nameplate?: any;
|
||||
iconForeground?: string;
|
||||
hideTooltips?: boolean;
|
||||
}
|
||||
|
||||
export type UserAreaButtonFactory = (props: UserAreaRenderProps) => ReactNode;
|
||||
|
||||
export interface UserAreaButtonData {
|
||||
render: UserAreaButtonFactory;
|
||||
icon: ComponentType<{ className?: string; }>;
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
interface ButtonEntry {
|
||||
render: UserAreaButtonFactory;
|
||||
priority: number;
|
||||
}
|
||||
|
||||
export const UserAreaButton = PanelButton;
|
||||
|
||||
const logger = new Logger("UserArea");
|
||||
|
||||
export const buttons = new Map<string, ButtonEntry>();
|
||||
|
||||
export function addUserAreaButton(id: string, render: UserAreaButtonFactory, priority = 0) {
|
||||
buttons.set(id, { render, priority });
|
||||
}
|
||||
|
||||
export function removeUserAreaButton(id: string) {
|
||||
buttons.delete(id);
|
||||
}
|
||||
|
||||
function UserAreaButtons({ props }: { props: UserAreaRenderProps; }) {
|
||||
return (
|
||||
<>
|
||||
{Array.from(buttons)
|
||||
.sort(([, a], [, b]) => a.priority - b.priority)
|
||||
.map(([id, { render: Button }]) => (
|
||||
<ErrorBoundary noop key={id} onError={e => logger.error(`Failed to render ${id}`, e.error)}>
|
||||
<Button {...props} />
|
||||
</ErrorBoundary>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function _renderButtons(props: UserAreaRenderProps) {
|
||||
return [<UserAreaButtons key="vc-user-area-buttons" props={props} />];
|
||||
}
|
||||
|
|
@ -32,6 +32,7 @@ import * as $MessageUpdater from "./MessageUpdater";
|
|||
import * as $NicknameIcons from "./NicknameIcons";
|
||||
import * as $Notices from "./Notices";
|
||||
import * as $Notifications from "./Notifications";
|
||||
import * as $UserArea from "./UserArea";
|
||||
export * as PluginManager from "./PluginManager";
|
||||
import * as $ServerList from "./ServerList";
|
||||
import * as $Settings from "./Settings";
|
||||
|
|
@ -160,6 +161,11 @@ export const NicknameIcons = $NicknameIcons;
|
|||
*/
|
||||
export const AudioPlayer = $AudioPlayer;
|
||||
|
||||
/**
|
||||
* An API allowing you to add buttons to the user area panel.
|
||||
*/
|
||||
export const UserArea = $UserArea;
|
||||
|
||||
/**
|
||||
* Just used to identify if user is on Equicord as Vencord doesn't have this.
|
||||
*/
|
||||
|
|
|
|||
36
src/equicordplugins/_api/userArea.ts
Normal file
36
src/equicordplugins/_api/userArea.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "UserAreaAPI",
|
||||
description: "API to add buttons to the user area panel.",
|
||||
authors: [EquicordDevs.Prism],
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
replacement: {
|
||||
match: /(className:(\i)\.buttons,.{0,50}?children:\[)/,
|
||||
replace: "$1...$self.renderButtons(arguments[0],$2),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
renderButtons(props: { nameplate?: any; }, styles: { iconForeground?: string; }) {
|
||||
return Vencord.Api.UserArea._renderButtons({
|
||||
nameplate: props.nameplate,
|
||||
iconForeground: props.nameplate != null ? styles.iconForeground : void 0,
|
||||
hideTooltips: this.shouldHideTooltips()
|
||||
});
|
||||
},
|
||||
|
||||
shouldHideTooltips() {
|
||||
return Vencord.Plugins.isPluginEnabled("BetterUserArea") && Vencord.Plugins.plugins.BetterUserArea.settings?.store?.removeButtonTooltips;
|
||||
}
|
||||
});
|
||||
17
src/equicordplugins/equicordHelper/pluginCards.css
Normal file
17
src/equicordplugins/equicordHelper/pluginCards.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.vc-plugins-management-cards.vc-plugins-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.vc-plugins-management-cards .vc-addon-card {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.vc-plugins-management-cards .vc-addon-title-container {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.vc-plugins-management-cards .vc-addon-title {
|
||||
position: static;
|
||||
overflow: visible;
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./pluginCards.css";
|
||||
|
||||
import { isPluginEnabled, isPluginRequired } from "@api/PluginManager";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
|
|
|
|||
|
|
@ -4,19 +4,17 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./styles.css";
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { UserAreaButton, UserAreaRenderProps } from "@api/UserArea";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack";
|
||||
import { ChannelActions, ChannelRouter, ChannelStore, ContextMenuApi, GuildStore, MediaEngineStore, Menu, PermissionsBits, PermissionStore, React, SelectedChannelStore, Toasts, UserStore, VoiceActions, VoiceStateStore } from "@webpack/common";
|
||||
|
||||
import style from "./styles.css?managed";
|
||||
|
||||
const ChatVoiceIcon = findComponentByCodeLazy("0l1.8-1.8c.17");
|
||||
const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON");
|
||||
const startStream = findByCodeLazy('type:"STREAM_START"');
|
||||
const getDesktopSources = findByCodeLazy("desktop sources");
|
||||
|
||||
|
|
@ -160,21 +158,28 @@ interface VoiceState {
|
|||
requestToSpeakTimestamp: string | null;
|
||||
}
|
||||
|
||||
function RandomVoiceIcon({ className }: { className?: string; }) {
|
||||
return (
|
||||
<svg className={className} width="18" height="18" viewBox="0 0 24 24">
|
||||
<g fill="currentColor">
|
||||
<path d="M19,9H14a5.006,5.006,0,0,0-5,5v5a5.006,5.006,0,0,0,5,5h5a5.006,5.006,0,0,0,5-5V14A5.006,5.006,0,0,0,19,9Zm-5,6a1,1,0,1,1,1-1A1,1,0,0,1,14,15Zm5,5a1,1,0,1,1,1-1A1,1,0,0,1,19,20ZM15.6,5,12.069,1.462A5.006,5.006,0,0,0,5,1.462L1.462,5a5.006,5.006,0,0,0,0,7.071L5,15.6a4.961,4.961,0,0,0,2,1.223V14a7.008,7.008,0,0,1,7-7h2.827A4.961,4.961,0,0,0,15.6,5ZM5,10A1,1,0,1,1,6,9,1,1,0,0,1,5,10ZM9,6a1,1,0,1,1,1-1A1,1,0,0,1,9,6Z" />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "RandomVoice",
|
||||
description: "Adds a Button near the Mute button to join a random voice call.",
|
||||
authors: [EquicordDevs.xijexo, EquicordDevs.omaw, Devs.thororen],
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
replacement: {
|
||||
match: /className:\i\.buttons,.{0,60}children:\[/,
|
||||
replace: "$&$self.randomVoice(),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
userAreaButton: {
|
||||
icon: RandomVoiceIcon,
|
||||
render: RandomVoiceButton
|
||||
},
|
||||
|
||||
flux: {
|
||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
VOICE_STATE_UPDATES() {
|
||||
const currentUserId = UserStore.getCurrentUser().id;
|
||||
const myChannelId = VoiceStateStore.getVoiceStateForUser(currentUserId)?.channelId;
|
||||
if (!myChannelId || !settings.store.leaveEmpty) return;
|
||||
|
|
@ -185,40 +190,24 @@ export default definePlugin({
|
|||
);
|
||||
|
||||
if (othersInChannel.length === 0) {
|
||||
randomVoice();
|
||||
getChannels();
|
||||
}
|
||||
},
|
||||
},
|
||||
start() {
|
||||
enableStyle(style);
|
||||
},
|
||||
stop() {
|
||||
disableStyle(style);
|
||||
},
|
||||
|
||||
settings,
|
||||
randomVoice: ErrorBoundary.wrap(randomVoice, { noop: true }),
|
||||
});
|
||||
|
||||
function randomVoice() {
|
||||
function RandomVoiceButton({ iconForeground, hideTooltips, nameplate }: UserAreaRenderProps) {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onContextMenu={e => ContextMenuApi.openContextMenu(e, () => <ContextMenu />)}
|
||||
onClick={() => getChannels()}
|
||||
role="switch"
|
||||
tooltipText={"Random Voice"}
|
||||
icon={<svg
|
||||
width="18"
|
||||
height="18"
|
||||
id="test"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<g fill={"currentColor"}>
|
||||
<path d="M19,9H14a5.006,5.006,0,0,0-5,5v5a5.006,5.006,0,0,0,5,5h5a5.006,5.006,0,0,0,5-5V14A5.006,5.006,0,0,0,19,9Zm-5,6a1,1,0,1,1,1-1A1,1,0,0,1,14,15Zm5,5a1,1,0,1,1,1-1A1,1,0,0,1,19,20ZM15.6,5,12.069,1.462A5.006,5.006,0,0,0,5,1.462L1.462,5a5.006,5.006,0,0,0,0,7.071L5,15.6a4.961,4.961,0,0,0,2,1.223V14a7.008,7.008,0,0,1,7-7h2.827A4.961,4.961,0,0,0,15.6,5ZM5,10A1,1,0,1,1,6,9,1,1,0,0,1,5,10ZM9,6a1,1,0,1,1,1-1A1,1,0,0,1,9,6Z" />
|
||||
</g>
|
||||
</svg>}
|
||||
/>
|
||||
</>
|
||||
<UserAreaButton
|
||||
onContextMenu={e => ContextMenuApi.openContextMenu(e, () => <ContextMenu />)}
|
||||
onClick={() => getChannels()}
|
||||
role="switch"
|
||||
tooltipText={hideTooltips ? void 0 : "Random Voice"}
|
||||
icon={<RandomVoiceIcon className={iconForeground} />}
|
||||
plated={nameplate != null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -320,8 +309,7 @@ function ContextMenu() {
|
|||
height={"22"}
|
||||
viewBox={"0 0 26 26"}
|
||||
>
|
||||
<g fill={"#b5bac1"}
|
||||
>
|
||||
<g fill="currentColor">
|
||||
<path d="M12,2a10.032,10.032,0,0,1,7.122,3H16a1,1,0,0,0-1,1h0a1,1,0,0,0,1,1h4.143A1.858,1.858,0,0,0,22,5.143V1a1,1,0,0,0-1-1h0a1,1,0,0,0-1,1V3.078A11.981,11.981,0,0,0,.05,10.9a1.007,1.007,0,0,0,1,1.1h0a.982.982,0,0,0,.989-.878A10.014,10.014,0,0,1,12,2Z" /><path d="M22.951,12a.982.982,0,0,0-.989.878A9.986,9.986,0,0,1,4.878,19H8a1,1,0,0,0,1-1H9a1,1,0,0,0-1-1H3.857A1.856,1.856,0,0,0,2,18.857V23a1,1,0,0,0,1,1H3a1,1,0,0,0,1-1V20.922A11.981,11.981,0,0,0,23.95,13.1a1.007,1.007,0,0,0-1-1.1Z" />
|
||||
</g>
|
||||
|
||||
|
|
@ -589,7 +577,6 @@ function ContextMenu() {
|
|||
>
|
||||
<Menu.MenuItem id="voiceOptions" label="Voice Options" action={() => { }} >
|
||||
<>
|
||||
{ }
|
||||
<Menu.MenuCheckboxItem
|
||||
key="selfMute"
|
||||
id="selfMute"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
[class^="buttons__"] > [class^="buttons__"]:not([class^="expression-picker"]) {
|
||||
[class^="buttons__"]>[class^="buttons__"]:not([class^="expression-picker"]) {
|
||||
width: 150px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
[class^="micButtonParent__"] {
|
||||
width: 16px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,24 +6,23 @@
|
|||
|
||||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { UserAreaButton, UserAreaRenderProps } from "@api/UserArea";
|
||||
import equicordToolbox from "@equicordplugins/equicordToolbox";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Constants, Menu, React, RestAPI, useEffect, useState } from "@webpack/common";
|
||||
import { Constants, Menu, RestAPI, useEffect, useState } from "@webpack/common";
|
||||
|
||||
let spotifyId: string | null = null;
|
||||
let showActivity: boolean = false;
|
||||
let showActivity = false;
|
||||
let isLoaded = false;
|
||||
let forceUpdate: (() => void) | null = null;
|
||||
|
||||
const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON");
|
||||
|
||||
function makeSpotifyIcon(enabled: boolean) {
|
||||
function SpotifyIcon({ className, enabled }: { className?: string; enabled: boolean; }) {
|
||||
const redLinePath = "M48.54 4.54a2.2 2.2 0 0 0-3.08-3.08l-44 44a2.2 2.2 0 1 0 3.08 3.08Z";
|
||||
const maskBlackPath = "M50.376 8.248 41.752-.376-.376 41.752 8.248 50.376Z";
|
||||
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 50 50">
|
||||
<svg className={className} width="20" height="20" viewBox="0 0 50 50">
|
||||
<path
|
||||
fill={!enabled ? "var(--status-danger)" : "currentColor"}
|
||||
mask={!enabled ? "url(#spotifyActivityMask)" : void 0}
|
||||
|
|
@ -40,34 +39,32 @@ function makeSpotifyIcon(enabled: boolean) {
|
|||
);
|
||||
}
|
||||
|
||||
function SpotifyActivityToggleButton(props: { nameplate?: any; }) {
|
||||
function SpotifyActivityToggleButton({ iconForeground, hideTooltips, nameplate }: UserAreaRenderProps) {
|
||||
const { location } = settings.use(["location"]);
|
||||
const [showActivityState, setShowActivityState] = useState(showActivity);
|
||||
const [, rerender] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setShowActivityState(showActivity);
|
||||
forceUpdate = () => rerender(n => n + 1);
|
||||
return () => { forceUpdate = null; };
|
||||
}, []);
|
||||
|
||||
if (showActivity === null) return null;
|
||||
if (!spotifyId || location !== "PANEL" && isPluginEnabled(equicordToolbox.name)) return null;
|
||||
if (!isLoaded || !spotifyId || location !== "PANEL" && isPluginEnabled(equicordToolbox.name)) return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
tooltipText={showActivityState ? "Turn off Spotify activity" : "Turn on Spotify activity"}
|
||||
icon={makeSpotifyIcon(showActivityState)}
|
||||
<UserAreaButton
|
||||
tooltipText={hideTooltips ? void 0 : showActivity ? "Turn off Spotify activity" : "Turn on Spotify activity"}
|
||||
icon={<SpotifyIcon className={iconForeground} enabled={showActivity} />}
|
||||
role="switch"
|
||||
aria-checked={showActivityState}
|
||||
redGlow={!showActivityState}
|
||||
plated={props?.nameplate != null}
|
||||
aria-checked={showActivity}
|
||||
redGlow={!showActivity}
|
||||
plated={nameplate != null}
|
||||
onClick={async () => {
|
||||
const newValue = !showActivityState;
|
||||
setShowActivityState(newValue);
|
||||
showActivity = !showActivity;
|
||||
forceUpdate?.();
|
||||
|
||||
await RestAPI.patch({
|
||||
url: Constants.Endpoints.CONNECTION("spotify", spotifyId),
|
||||
body: {
|
||||
show_activity: newValue,
|
||||
},
|
||||
body: { show_activity: showActivity },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
|
@ -77,7 +74,7 @@ function SpotifyActivityToggleButton(props: { nameplate?: any; }) {
|
|||
const settings = definePluginSettings({
|
||||
location: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Where to show the spotify toggle button",
|
||||
description: "Where to show the Spotify toggle button.",
|
||||
options: [
|
||||
{ label: "Next to Mute/Deafen", value: "PANEL", default: true },
|
||||
{ label: "Equicord Toolbox", value: "TOOLBOX" }
|
||||
|
|
@ -93,15 +90,12 @@ export default definePlugin({
|
|||
description: "Adds a toggle button for Spotify activity visibility.",
|
||||
authors: [Devs.thororen],
|
||||
settings,
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
replacement: {
|
||||
match: /className:\i\.buttons,.{0,50}children:\[/,
|
||||
replace: "$&$self.SpotifyActivityToggleButton(arguments[0]),"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
userAreaButton: {
|
||||
icon: (props: { className?: string; }) => <SpotifyIcon {...props} enabled={showActivity} />,
|
||||
render: SpotifyActivityToggleButton
|
||||
},
|
||||
|
||||
toolboxActions() {
|
||||
const { location } = settings.use(["location"]);
|
||||
|
||||
|
|
@ -112,28 +106,29 @@ export default definePlugin({
|
|||
id="spotify-activity-toggle-toolbox"
|
||||
label={showActivity ? "Disable Spotify Activity" : "Enable Spotify Activity"}
|
||||
action={async () => {
|
||||
const newValue = !showActivity;
|
||||
showActivity = newValue;
|
||||
showActivity = !showActivity;
|
||||
|
||||
await RestAPI.patch({
|
||||
url: Constants.Endpoints.CONNECTION("spotify", spotifyId),
|
||||
body: { show_activity: newValue },
|
||||
body: { show_activity: showActivity },
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
async start() {
|
||||
const { body } = await RestAPI.get({
|
||||
url: Constants.Endpoints.CONNECTIONS,
|
||||
});
|
||||
if (!body) return;
|
||||
|
||||
const spotifyConn = body.find(c => c.type === "spotify");
|
||||
if (spotifyConn) {
|
||||
spotifyId = spotifyConn.id;
|
||||
showActivity = spotifyConn.show_activity;
|
||||
flux: {
|
||||
async CONNECTION_OPEN() {
|
||||
const { body } = await RestAPI.get({ url: Constants.Endpoints.CONNECTIONS });
|
||||
if (!body) return;
|
||||
|
||||
const spotifyConn = body.find((c: { type: string; }) => c.type === "spotify");
|
||||
if (spotifyConn) {
|
||||
spotifyId = spotifyConn.id;
|
||||
showActivity = spotifyConn.show_activity;
|
||||
}
|
||||
isLoaded = true;
|
||||
forceUpdate?.();
|
||||
}
|
||||
},
|
||||
SpotifyActivityToggleButton: ErrorBoundary.wrap(SpotifyActivityToggleButton, { noop: true }),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,20 +16,17 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import "./style.css";
|
||||
|
||||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { UserAreaButton, UserAreaRenderProps } from "@api/UserArea";
|
||||
import { getUserSettingLazy } from "@api/UserSettings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import equicordToolbox from "@equicordplugins/equicordToolbox";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findComponentByCodeLazy } from "@webpack";
|
||||
import { Menu } from "@webpack/common";
|
||||
|
||||
import managedStyle from "./style.css?managed";
|
||||
|
||||
const Button = findComponentByCodeLazy(".NONE,disabled:", ".PANEL_BUTTON");
|
||||
|
||||
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
|
|
@ -51,11 +48,10 @@ const settings = definePluginSettings({
|
|||
}
|
||||
});
|
||||
|
||||
function Icon() {
|
||||
function Icon({ className }: { className?: string; }) {
|
||||
const { oldIcon } = settings.use(["oldIcon"]);
|
||||
const showCurrentGame = ShowCurrentGame.useSetting();
|
||||
|
||||
|
||||
const redLinePath = !oldIcon
|
||||
? "M22.7 2.7a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4Z"
|
||||
: "M23 2.27 21.73 1 1 21.73 2.27 23 23 2.27Z";
|
||||
|
|
@ -65,7 +61,7 @@ function Icon() {
|
|||
: "M23.27 4.54 19.46.73 .73 19.46 4.54 23.27 23.27 4.54Z";
|
||||
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 24 24">
|
||||
<svg className={className} width="20" height="20" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill={!showCurrentGame && !oldIcon ? "var(--status-danger)" : "currentColor"}
|
||||
mask={!showCurrentGame ? "url(#gameActivityMask)" : void 0}
|
||||
|
|
@ -82,21 +78,20 @@ function Icon() {
|
|||
);
|
||||
}
|
||||
|
||||
function GameActivityToggleButton(props: { nameplate?: any; }) {
|
||||
function GameActivityToggleButton({ iconForeground, hideTooltips, nameplate }: UserAreaRenderProps) {
|
||||
const { location } = settings.use(["location"]);
|
||||
const showCurrentGame = ShowCurrentGame.useSetting();
|
||||
|
||||
if (location !== "PANEL" && isPluginEnabled(equicordToolbox.name)) return null;
|
||||
|
||||
return (
|
||||
<Button
|
||||
className="vc-game-activity"
|
||||
tooltipText={showCurrentGame ? "Disable Game Activity" : "Enable Game Activity"}
|
||||
icon={Icon}
|
||||
<UserAreaButton
|
||||
tooltipText={hideTooltips ? void 0 : showCurrentGame ? "Disable Game Activity" : "Enable Game Activity"}
|
||||
icon={<Icon className={iconForeground} />}
|
||||
role="switch"
|
||||
aria-checked={!showCurrentGame}
|
||||
redGlow={!showCurrentGame}
|
||||
plated={props?.nameplate != null}
|
||||
plated={nameplate != null}
|
||||
onClick={() => ShowCurrentGame.updateSetting(old => !old)}
|
||||
/>
|
||||
);
|
||||
|
|
@ -109,17 +104,10 @@ export default definePlugin({
|
|||
dependencies: ["UserSettingsAPI"],
|
||||
settings,
|
||||
|
||||
managedStyle,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
|
||||
replacement: {
|
||||
match: /className:\i\.buttons,.{0,50}children:\[/,
|
||||
replace: "$&$self.GameActivityToggleButton(arguments[0]),"
|
||||
}
|
||||
}
|
||||
],
|
||||
userAreaButton: {
|
||||
icon: Icon,
|
||||
render: GameActivityToggleButton
|
||||
},
|
||||
|
||||
toolboxActions() {
|
||||
const { location } = settings.use(["location"]);
|
||||
|
|
@ -136,6 +124,4 @@ export default definePlugin({
|
|||
/>
|
||||
);
|
||||
},
|
||||
|
||||
GameActivityToggleButton: ErrorBoundary.wrap(GameActivityToggleButton, { noop: true }),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
[class^="panels"] [class^="avatarWrapper_"] {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import type { MessageDecorationFactory } from "@api/MessageDecorations";
|
|||
import type { MessageClickListener, MessageEditListener, MessageSendListener } from "@api/MessageEvents";
|
||||
import type { MessagePopoverButtonData, MessagePopoverButtonFactory } from "@api/MessagePopover";
|
||||
import type { NicknameIconFactory } from "@api/NicknameIcons";
|
||||
import type { UserAreaButtonData } from "@api/UserArea";
|
||||
import type { Command, FluxEvents } from "@vencord/discord-types";
|
||||
import type { ReactNode } from "react";
|
||||
import type { LiteralUnion } from "type-fest";
|
||||
|
|
@ -206,6 +207,7 @@ export interface PluginDef {
|
|||
renderNicknameIcon?: NicknameIconFactory;
|
||||
headerBarButton?: HeaderBarButtonData;
|
||||
audioProcessor?: AudioProcessor;
|
||||
userAreaButton?: UserAreaButtonData;
|
||||
|
||||
// TODO: Remove eventually
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue