Update VPN client and gateway behavior
Build and Deploy Gateway / build-and-push (push) Successful in 19s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-11 08:50:44 +03:00
parent 396c5d1917
commit 79ffff194f
35 changed files with 1036 additions and 590 deletions
@@ -19,6 +19,8 @@ import {
} from '../../utils/clientControls.js';
import { formatBytes } from '../../utils/format.js';
import { ConfirmationDialog } from '../../ui/ConfirmationDialog.js';
import { Drawer } from '../../ui/Drawer.js';
import { RailAction } from '../../ui/RailAction.js';
import type { RequestError } from './requestError.js';
interface ProfileError extends RequestError {
@@ -75,7 +77,7 @@ export function useSubscriptionFeature({
const [deleteId, setDeleteId] = useState('');
const [refreshingIds, setRefreshingIds] = useState<string[]>([]);
const [revealVersions, setRevealVersions] = useState<Record<string, number>>({});
const panelRef = useRef<HTMLDivElement>(null);
const panelRef = useRef<HTMLElement>(null);
const toggleRef = useRef<HTMLButtonElement>(null);
const closeRef = useRef<HTMLButtonElement>(null);
const labelRef = useRef<HTMLInputElement>(null);
@@ -307,20 +309,19 @@ export function SubscriptionToggle({
feature: SubscriptionFeatureController;
onToggle: () => void;
}) {
return <button
ref={feature.toggleRef}
className={`client-instructions-toggle client-subscription-toggle${feature.open ? ' is-open' : ''}`}
type="button"
aria-expanded={feature.open}
aria-controls="client-subscription-drawer"
aria-label={feature.open ? 'Закрыть подписки' : 'Управление подписками'}
return <RailAction
buttonRef={feature.toggleRef}
className="client-instructions-toggle client-subscription-toggle"
open={feature.open}
controls="client-subscription-drawer"
ariaLabel={feature.open ? 'Закрыть подписки' : 'Управление подписками'}
label="Подписки"
onClick={onToggle}
>
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 5h14v14H5zM8 9h8M8 13h5" />
</svg>
<span>Подписки</span>
</button>;
</RailAction>;
}
function AddProfileForm({ feature }: { feature: SubscriptionFeatureController }) {
@@ -514,44 +515,38 @@ export function SubscriptionPanel({
<AddProfileForm feature={feature} />
</div>}
<div
ref={feature.panelRef}
<Drawer
panelRef={feature.panelRef}
closeRef={feature.closeRef}
id="client-subscription-drawer"
className={`client-drawer client-subscription-drawer${drawerOpen ? ' is-open' : ''}`}
aria-label="Управление подписками"
aria-hidden={!drawerOpen}
inert={!drawerOpen ? true : undefined}
className="client-subscription-drawer"
sheetClassName="client-subscription-sheet"
open={drawerOpen}
label="Управление подписками"
closeLabel="Закрыть подписки"
onClose={feature.close}
>
<div className="client-drawer-sheet client-subscription-sheet">
<header className="client-profiles-header">
<h2>ПОДПИСКИ</h2>
<button
ref={feature.closeRef}
className="client-drawer-close"
type="button"
aria-label="Закрыть подписки"
onClick={feature.close}
>×</button>
</header>
{statusSlot}
<div className="client-profile-list">
{feature.profiles.map((profile) => <ProfileGroup
key={profile.id}
feature={feature}
profile={profile}
renderServerPicker={renderServerPicker}
/>)}
</div>
{(feature.adding || feature.addClosing) && feature.profiles.length > 0
? <AddProfileForm feature={feature} />
: <button
id="client-profile-add-trigger"
className="client-profile-add-trigger"
type="button"
onClick={feature.showAdd}
> Добавить подписку</button>}
<header className="client-profiles-header">
<h2>ПОДПИСКИ</h2>
</header>
{statusSlot}
<div className="client-profile-list">
{feature.profiles.map((profile) => <ProfileGroup
key={profile.id}
feature={feature}
profile={profile}
renderServerPicker={renderServerPicker}
/>)}
</div>
</div>
{(feature.adding || feature.addClosing) && feature.profiles.length > 0
? <AddProfileForm feature={feature} />
: <button
id="client-profile-add-trigger"
className="client-profile-add-trigger"
type="button"
onClick={feature.showAdd}
> Добавить подписку</button>}
</Drawer>
</>;
}