Add stop confirmation to client power action
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
export const HARBOR_VERSIONS = Object.freeze({
|
||||
macClient: '0.8.10',
|
||||
gatewayClient: '0.8.9',
|
||||
macClient: '0.8.11',
|
||||
gatewayClient: '0.8.10',
|
||||
gatewayBackend: '0.8.0',
|
||||
});
|
||||
|
||||
|
||||
@@ -644,6 +644,7 @@ export function ClientOverviewPage({
|
||||
const [localRulesRevision, setLocalRulesRevision] = useState(state?.route?.localRulesRevision || 0);
|
||||
const [confirmingLocalRulesClose, setConfirmingLocalRulesClose] = useState(false);
|
||||
const [confirmingDelete, setConfirmingDelete] = useState(false);
|
||||
const [confirmingStop, setConfirmingStop] = useState(false);
|
||||
const [openInstructionId, setOpenInstructionId] = useState('');
|
||||
const subscriptionInputRef = useRef(null);
|
||||
const subscriptionRef = useRef(null);
|
||||
@@ -899,13 +900,21 @@ export function ClientOverviewPage({
|
||||
return () => document.removeEventListener('pointerdown', closeInstructionsOutside);
|
||||
}, [instructionsOpen]);
|
||||
|
||||
async function toggleConnection() {
|
||||
function toggleConnection() {
|
||||
const action = connectionAction({ connected, selectedServerId, configExists: state?.configExists });
|
||||
if (action?.type === 'stop') return onStop();
|
||||
if (action?.type === 'stop') {
|
||||
setConfirmingStop(true);
|
||||
return;
|
||||
}
|
||||
if (action?.type === 'apply') return onApply(action.serverId);
|
||||
if (action?.type === 'restart') return onRestart();
|
||||
}
|
||||
|
||||
async function stopConnection() {
|
||||
if (!await onStop()) return;
|
||||
setConfirmingStop(false);
|
||||
}
|
||||
|
||||
function selectServer(serverId) {
|
||||
setPendingServerId(serverId);
|
||||
if (connected && serverId) onApply(serverId);
|
||||
@@ -1443,6 +1452,18 @@ export function ClientOverviewPage({
|
||||
onClose={requestCloseLocalRules}
|
||||
onSave={saveLocalRules}
|
||||
/>}
|
||||
<ConfirmationPopup
|
||||
open={confirmingStop}
|
||||
id="stop-connection"
|
||||
kicker="Защита от случайного отключения"
|
||||
title="Отключить VPN?"
|
||||
description="Harbor остановит текущее VPN-подключение. Локальный прокси перестанет передавать трафик до повторного включения."
|
||||
cancelLabel="Оставить включённым"
|
||||
confirmLabel="Отключить VPN"
|
||||
busy={connectionBlocked}
|
||||
onCancel={() => setConfirmingStop(false)}
|
||||
onConfirm={stopConnection}
|
||||
/>
|
||||
<ConfirmationPopup
|
||||
open={confirmingLocalRulesClose}
|
||||
id="discard-local-rules"
|
||||
|
||||
@@ -40,6 +40,7 @@ test('rule editor add latency stays constant and dirty exits are guarded', () =>
|
||||
});
|
||||
|
||||
test('critical confirmations share one accessible blocking popup', () => {
|
||||
assert.match(component, /id="stop-connection"/);
|
||||
assert.match(component, /id="discard-local-rules"/);
|
||||
assert.match(component, /id="delete-subscription"/);
|
||||
assert.doesNotMatch(component, /client-local-rules-discard|client-delete-confirmation/);
|
||||
@@ -47,9 +48,17 @@ test('critical confirmations share one accessible blocking popup', () => {
|
||||
assert.match(popup, /aria-modal="true"/);
|
||||
assert.match(popup, /querySelectorAll\(FOCUSABLE\)/);
|
||||
assert.match(popup, /element\.inert = true/);
|
||||
assert.match(popup, /requestAnimationFrame\(\(\) => cancelRef\.current\?\.focus\(\)\)/);
|
||||
assert.match(styles, /\.client-confirmation-popup\.is-open[\s\S]*backdrop-filter: blur\(18px\)/);
|
||||
});
|
||||
|
||||
test('power click and native Enter cannot stop VPN without a separate confirmation', () => {
|
||||
assert.match(component, /className="client-power"[\s\S]*type="button"[\s\S]*onClick=\{toggleConnection\}/);
|
||||
assert.match(component, /if \(action\?\.type === 'stop'\) \{[\s\S]*setConfirmingStop\(true\);[\s\S]*return;[\s\S]*\}/);
|
||||
assert.doesNotMatch(component, /if \(action\?\.type === 'stop'\) return onStop\(\)/);
|
||||
assert.match(component, /id="stop-connection"[\s\S]*cancelLabel="Оставить включённым"[\s\S]*confirmLabel="Отключить VPN"[\s\S]*onConfirm=\{stopConnection\}/);
|
||||
});
|
||||
|
||||
test('copy feedback, drawers and Gateway access actions expose complete semantics', () => {
|
||||
assert.match(component, /className="client-live-region" role="status" aria-live="polite" aria-atomic="true"/);
|
||||
assert.match(component, /Не удалось скопировать/);
|
||||
|
||||
Reference in New Issue
Block a user