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
@@ -1,5 +1,8 @@
import { useEffect, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import { CopyButton } from '../../ui/CopyButton.js';
import { Drawer } from '../../ui/Drawer.js';
import { RailAction } from '../../ui/RailAction.js';
import { copyText } from '../../utils/clientControls.js';
import { instructionBlocks } from './instructionBlocks.js';
@@ -135,16 +138,12 @@ function InstructionBlock({
const feedback = copyFeedback[action.id];
return <div className="client-instruction-copy" key={action.id}>
<span>{action.label}</span>
<button
className={`client-copy-button client-instruction-copy-button${feedback ? feedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
type="button"
<CopyButton
className="client-instruction-copy-button"
label="Скопировать"
feedback={feedback}
onClick={() => copyInstruction(action)}
>
<span className="client-copy-label">Скопировать</span>
{feedback && <span className="client-copy-feedback" aria-hidden="true">
{feedback.failed ? 'Ошибка' : 'Скопировано'}
</span>}
</button>
/>
</div>;
})}
<span className="client-live-region" role="status" aria-live="polite">
@@ -238,21 +237,20 @@ export function InstructionsToggle({
feature: InstructionsFeature;
onToggle: () => void;
}) {
return <button
ref={feature.toggleRef}
className={`client-instructions-toggle${feature.isOpen ? ' is-open' : ''}`}
type="button"
aria-expanded={feature.isOpen}
aria-controls="client-instructions"
aria-label={feature.isOpen ? 'Закрыть инструкции' : 'Как использовать'}
return <RailAction
buttonRef={feature.toggleRef}
className="client-instructions-toggle"
open={feature.isOpen}
controls="client-instructions"
ariaLabel={feature.isOpen ? 'Закрыть инструкции' : 'Как использовать'}
label="Как использовать"
onClick={onToggle}
>
<svg viewBox="0 0 24 24" aria-hidden="true">
<circle className="client-rail-info-ring" cx="12" cy="12" r="8.5" pathLength="1" />
<path d="M12 11v5M12 8h.01" />
</svg>
<span>Как использовать</span>
</button>;
</RailAction>;
}
export function InstructionsPanel({
@@ -262,40 +260,34 @@ export function InstructionsPanel({
feature: InstructionsFeature;
isGateway: boolean;
}) {
return <aside
ref={feature.panelRef}
return <Drawer
panelRef={feature.panelRef}
closeRef={feature.closeRef}
id="client-instructions"
className={`client-drawer client-instructions${feature.isOpen ? ' is-open' : ''}`}
aria-labelledby="instructions-title"
aria-hidden={!feature.isOpen}
inert={!feature.isOpen ? true : undefined}
className="client-instructions"
sheetClassName="client-instructions-sheet"
open={feature.isOpen}
labelledBy="instructions-title"
closeLabel="Закрыть инструкции"
onClose={feature.close}
>
<div className="client-drawer-sheet client-instructions-sheet">
<button
ref={feature.closeRef}
className="client-drawer-close"
type="button"
aria-label="Закрыть инструкции"
onClick={feature.close}
>×</button>
<header className="client-instructions-header">
<span>Подключение</span>
<h2 id="instructions-title">Как использовать {isGateway ? 'Gateway' : 'прокси'}</h2>
<div className="client-instructions-intro">
{feature.intro.paragraphs?.map((paragraph) => <p key={paragraph}>{paragraph}</p>)}
</div>
</header>
<div className="client-instruction-list">
{feature.guides.map((block) => (
<InstructionBlock
block={block}
key={block.id}
open={block.id === feature.openInstructionId}
onToggle={() => feature.toggleInstruction(block.id)}
/>
))}
<header className="client-instructions-header">
<span>Подключение</span>
<h2 id="instructions-title">Как использовать {isGateway ? 'Gateway' : 'прокси'}</h2>
<div className="client-instructions-intro">
{feature.intro.paragraphs?.map((paragraph) => <p key={paragraph}>{paragraph}</p>)}
</div>
</header>
<div className="client-instruction-list">
{feature.guides.map((block) => (
<InstructionBlock
block={block}
key={block.id}
open={block.id === feature.openInstructionId}
onToggle={() => feature.toggleInstruction(block.id)}
/>
))}
</div>
</aside>;
</Drawer>;
}