- Info panel in the plugin modal: live status with the number of keybinds GNOME registered, the three setup steps and a button that opens Discord's Keybinds page (closes only the plugin modal, which otherwise blocks settings navigation). - Follows Discord's language: English, Portuguese (pt-BR) and Spanish (es-ES and es-419 use each region's action names); bold labels quote Discord's own strings. - Shared helpers moved to discord.ts; copy and locale choice in copy.ts, covered by basis-path tests (41 tests). - README: current settings path (User Settings > System > Keybinds).
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
/*
|
|
* Vencord, a Discord client mod
|
|
* Copyright (c) 2026 Evandro
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
import { Button } from "@components/Button";
|
|
import { findByPropsLazy } from "@webpack";
|
|
import { i18n, SettingsRouter, useState, useStateFromStores } from "@webpack/common";
|
|
|
|
import { pickCopy } from "./copy";
|
|
import { KeybindsStore, portalActive } from "./discord";
|
|
|
|
function locale(): string {
|
|
try {
|
|
return i18n.intl.currentLocale ?? document.documentElement.lang;
|
|
} catch {
|
|
return document.documentElement.lang;
|
|
}
|
|
}
|
|
|
|
// the panel renders inside the plugin modal, so the top of Discord's modal stack at mount is that modal
|
|
const ModalStack = findByPropsLazy("getOpenModalKeys", "closeModal");
|
|
|
|
function topModalKey(): string | undefined {
|
|
try {
|
|
return [...ModalStack.getOpenModalKeys()].at(-1);
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function openKeybinds(pluginModalKey: string | undefined) {
|
|
// settings ignore navigation while another modal sits on top of them
|
|
if (pluginModalKey) ModalStack.closeModal(pluginModalKey);
|
|
SettingsRouter.openUserSettings("system_custom_keybinds_category");
|
|
}
|
|
|
|
function StatusIcon({ active }: { active: boolean; }) {
|
|
return (
|
|
<svg className="vc-wk-status-icon" viewBox="0 0 20 20" width="20" height="20" aria-hidden="true">
|
|
<circle cx="10" cy="10" r="8.25" fill="none" stroke="currentColor" strokeWidth="1.5" />
|
|
{active
|
|
? <path d="m6.5 10.25 2.4 2.4 4.6-5" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
|
|
: <path d="M10 9v4.5M10 6.25v.01" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" />}
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
export function AboutPanel() {
|
|
const copy = pickCopy(locale());
|
|
const active = portalActive();
|
|
const [modalKey] = useState(topModalKey);
|
|
const registered = useStateFromStores([KeybindsStore], () =>
|
|
Object.keys(KeybindsStore.getUserAgnosticState() ?? {})
|
|
.filter(id => KeybindsStore.getWaylandPortalShortcutState(id)?.bound).length
|
|
);
|
|
|
|
if (!active) {
|
|
return (
|
|
<p className="vc-wk-status">
|
|
<StatusIcon active={false} />
|
|
{copy.inactive}
|
|
</p>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<section className="vc-wk-panel" aria-labelledby="vc-wk-title">
|
|
<p className="vc-wk-status vc-wk-status-on">
|
|
<StatusIcon active />
|
|
{copy.active(registered)}
|
|
</p>
|
|
<h3 id="vc-wk-title" className="vc-wk-title">{copy.title}</h3>
|
|
<p className="vc-wk-lead">{copy.lead}</p>
|
|
{/* list-style is removed for the numbered badges, so keep the list role for screen readers */}
|
|
<ol className="vc-wk-steps" role="list">
|
|
{copy.steps.map(([before, bold, after], i) => <li key={i}><span>{before}{bold && <b>{bold}</b>}{after}</span></li>)}
|
|
</ol>
|
|
<div className="vc-wk-actions">
|
|
<Button onClick={() => openKeybinds(modalKey)}>{copy.open}</Button>
|
|
<p className="vc-wk-tip">{copy.tip}</p>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|