fix(frontend): qr code collapse — open only first panel, allow toggle

ClientQrModal and QrCodeModal both used activeKey without onChange,
forcing every panel open and blocking user toggle. Switch to controlled
state initialized to the first item's key on open, with onChange so
clicks update state.

Also remove unused AppBridge.tsx (superseded by per-page message.useMessage
hook).
This commit is contained in:
MHSanaei 2026-05-22 03:05:55 +02:00
parent 886376db7d
commit 2a96ac9721
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A
3 changed files with 27 additions and 23 deletions

View file

@ -1,11 +0,0 @@
import { useEffect } from 'react';
import { App } from 'antd';
import { setMessageInstance } from '@/utils/messageBus';
export default function AppBridge({ children }: { children: React.ReactNode }) {
const { message } = App.useApp();
useEffect(() => {
setMessageInstance(message);
}, [message]);
return <>{children}</>;
}

View file

@ -71,13 +71,7 @@ export default function ClientQrModal({
return () => { cancelled = true; };
}, [open, client?.subId]);
const activeKeys = useMemo(() => {
const keys: string[] = [];
if (subLink) keys.push('sub');
if (subJsonLink) keys.push('subJson');
if (links.length > 0) keys.push('l0');
return keys;
}, [subLink, subJsonLink, links.length]);
const [activeKey, setActiveKey] = useState<string[]>([]);
const items = useMemo(() => {
const out: { key: string; label: string; children: React.ReactNode }[] = [];
@ -105,6 +99,14 @@ export default function ClientQrModal({
return out;
}, [subLink, subJsonLink, links, client?.email, t]);
useEffect(() => {
if (!open) {
setActiveKey([]);
return;
}
setActiveKey(items.length > 0 ? [items[0].key] : []);
}, [open, items]);
return (
<Modal
open={open}
@ -121,7 +123,13 @@ export default function ClientQrModal({
{client?.subId && !hasAnything && !loading && (
<div style={{ padding: 24, textAlign: 'center', opacity: 0.6 }}>{t('pages.clients.noLinks')}</div>
)}
{hasAnything && <Collapse activeKey={activeKeys} accordion={false} items={items} />}
{hasAnything && (
<Collapse
activeKey={activeKey}
onChange={(keys) => setActiveKey(typeof keys === 'string' ? [keys] : (keys as string[]))}
items={items}
/>
)}
</Spin>
</Modal>
);

View file

@ -57,7 +57,7 @@ export default function QrCodeModal({
const [wireguardLinks, setWireguardLinks] = useState<string[]>([]);
const [subLink, setSubLink] = useState('');
const [subJsonLink, setSubJsonLink] = useState('');
const [defaultActive, setDefaultActive] = useState<string[]>([]);
const [activeKey, setActiveKey] = useState<string[]>([]);
useEffect(() => {
if (!open || !dbInbound) return;
@ -84,7 +84,6 @@ export default function QrCodeModal({
}
setSubLink(nextSub);
setSubJsonLink(nextSubJson);
setDefaultActive(nextSub ? ['sub'] : []);
}, [open, dbInbound, client, remarkModel, nodeAddress, subSettings]);
const qrItems = useMemo<QrItem[]>(() => {
@ -128,13 +127,21 @@ export default function QrCodeModal({
[qrItems],
);
useEffect(() => {
if (!open) {
setActiveKey([]);
return;
}
setActiveKey(qrItems.length > 0 ? [qrItems[0].key] : []);
}, [open, qrItems]);
return (
<Modal open={open} onCancel={onClose} title={t('qrCode')} footer={null} width={420} destroyOnHidden>
{dbInbound && collapseItems && collapseItems.length > 0 && (
<Collapse
key={collapseItems.map((i) => i?.key).join('|')}
ghost
defaultActiveKey={defaultActive}
activeKey={activeKey}
onChange={(keys) => setActiveKey(typeof keys === 'string' ? [keys] : (keys as string[]))}
items={collapseItems}
/>
)}