feat(plugin): Add MicLoopbackTester
* Refactor BetterUserTools to use UserAreaAPI * some changes * lol * lint * fix const * move files back * Update index.tsx * Update index.tsx * rename --------- Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
050dd1cdcc
commit
80bdd440b4
3 changed files with 135 additions and 1 deletions
|
|
@ -92,7 +92,7 @@
|
|||
"typescript-transform-paths": "^3.5.5",
|
||||
"zip-local": "^0.3.5"
|
||||
},
|
||||
"packageManager": "pnpm@10.26.0",
|
||||
"packageManager": "pnpm@10.26.1",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"@types/less@3.0.8": "patches/@types__less@3.0.8.patch"
|
||||
|
|
|
|||
130
src/equicordplugins/micLoopbackTester/index.tsx
Normal file
130
src/equicordplugins/micLoopbackTester/index.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { UserAreaButton, UserAreaButtonFactory, UserAreaRenderProps } from "@api/UserArea";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { MediaEngineStore, React, UserStore, VoiceActions, VoiceStateStore } from "@webpack/common";
|
||||
|
||||
let loopbackActive = false;
|
||||
let selfDeafenedByPlugin = false;
|
||||
|
||||
function isInVoiceChannel() {
|
||||
const id = UserStore.getCurrentUser()?.id;
|
||||
if (!id) return false;
|
||||
const state = VoiceStateStore.getVoiceStateForUser(id);
|
||||
return Boolean(state?.channelId);
|
||||
}
|
||||
|
||||
async function enableLoopback() {
|
||||
try {
|
||||
await VoiceActions.setLoopback("mic_test", true);
|
||||
loopbackActive = true;
|
||||
|
||||
if (isInVoiceChannel() && !MediaEngineStore.isSelfDeaf()) {
|
||||
await VoiceActions.toggleSelfDeaf();
|
||||
selfDeafenedByPlugin = true;
|
||||
} else {
|
||||
selfDeafenedByPlugin = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function disableLoopback() {
|
||||
try {
|
||||
await VoiceActions.setLoopback("mic_test", false);
|
||||
loopbackActive = false;
|
||||
|
||||
if (selfDeafenedByPlugin && MediaEngineStore.isSelfDeaf()) {
|
||||
await VoiceActions.toggleSelfDeaf();
|
||||
}
|
||||
selfDeafenedByPlugin = false;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
function MicLoopbackIcon({ active = false, className = "" }: { active?: boolean; className?: string; }) {
|
||||
const redLinePath = "M22.7 2.7a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4Z";
|
||||
const maskBlackPath = "M23.27 4.73 19.27 .73 -.27 20.27 3.73 24.27Z";
|
||||
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={!active ? "var(--status-danger)" : "currentColor"}
|
||||
mask={!active ? "url(#radarmask)" : void 0}
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h2.92l4.28 4.68a1 1 0 0 0 .74.32H11a1 1 0 0 0 1-1V3ZM15.1 20.75c-.58.14-1.1-.33-1.1-.92v-.03c0-.5.37-.92.85-1.05a7 7 0 0 0 0-13.5A1.11 1.11 0 0 1 14 4.2v-.03c0-.6.52-1.06 1.1-.92a9 9 0 0 1 0 17.5Z"
|
||||
/>
|
||||
<path
|
||||
fill={!active ? "var(--status-danger)" : "currentColor"}
|
||||
mask={!active ? "url(#radarmask)" : void 0}
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M15.16 16.51c-.57.28-1.16-.2-1.16-.83v-.14c0-.43.28-.8.63-1.02a3 3 0 0 0 0-5.04c-.35-.23-.63-.6-.63-1.02v-.14c0-.63.59-1.1 1.16-.83a5 5 0 0 1 0 9.02Z"
|
||||
/>
|
||||
{!active && <>
|
||||
<path fill="var(--status-danger)" d={redLinePath} />
|
||||
<mask id="radarmask">
|
||||
<rect fill="white" x="0" y="0" width="24" height="24" />
|
||||
<path fill="black" d={maskBlackPath} />
|
||||
</mask>
|
||||
</>}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function MicLoopbackButton({ iconForeground, hideTooltips, nameplate }: UserAreaRenderProps) {
|
||||
const [, forceUpdate] = React.useReducer(x => x + 1, 0);
|
||||
|
||||
const handleToggle = React.useCallback(async () => {
|
||||
if (loopbackActive) {
|
||||
await disableLoopback();
|
||||
} else {
|
||||
await enableLoopback();
|
||||
}
|
||||
forceUpdate();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<UserAreaButton
|
||||
tooltipText={hideTooltips ? void 0 : "Mic Test Loopback"}
|
||||
icon={<MicLoopbackIcon active={loopbackActive} className={iconForeground} />}
|
||||
role="switch"
|
||||
aria-checked={!loopbackActive}
|
||||
redGlow={!loopbackActive}
|
||||
plated={nameplate != null}
|
||||
onClick={handleToggle}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const MicLoopbackUserAreaButton: UserAreaButtonFactory = props => <MicLoopbackButton {...props} />;
|
||||
|
||||
export default definePlugin({
|
||||
name: "MicLoopbackTester",
|
||||
description: "Adds mic loopback test icon to the user panel",
|
||||
authors: [EquicordDevs.benjii],
|
||||
dependencies: ["UserSettingsAPI", "UserAreaAPI"],
|
||||
userAreaButton: {
|
||||
icon: MicLoopbackIcon,
|
||||
render: MicLoopbackUserAreaButton
|
||||
},
|
||||
|
||||
stop() {
|
||||
void disableLoopback();
|
||||
loopbackActive = false;
|
||||
selfDeafenedByPlugin = false;
|
||||
},
|
||||
});
|
||||
|
|
@ -1217,6 +1217,10 @@ export const EquicordDevs = Object.freeze({
|
|||
name: "soap phia",
|
||||
id: 1012095822957133976n
|
||||
},
|
||||
benjii: {
|
||||
name: "Benjii",
|
||||
id: 463702169443368970n
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue