Animate Harbor brand swap arrow and bump client versions
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 14s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-07-12 12:47:37 +03:00
parent 394fceac15
commit 2d1d89911e
3 changed files with 20 additions and 24 deletions

View File

@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.7.7',
gatewayClient: '0.7.7',
macClient: '0.7.8',
gatewayClient: '0.7.8',
gatewayBackend: '0.7.0',
});

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
import { api } from '../api.js';
import {
@@ -483,13 +483,21 @@ function AnimatedSeconds({ value, padded = true }) {
function HarborBrand({ isGateway, gatewayAvailable, gatewayDirect, blocked, onSetGatewayAuto }) {
const [modeAnimating, setModeAnimating] = useState(false);
const [arrowTurns, setArrowTurns] = useState(gatewayDirect ? 0.5 : 0);
const stopModeAnimationRef = useRef(false);
const previousGatewayDirectRef = useRef(gatewayDirect);
const product = isGateway ? 'Gateway' : 'Connect';
const switchable = !isGateway && gatewayAvailable;
const label = gatewayDirect
? 'Игнорировать Harbor Gateway и использовать локальный VPN'
: 'Использовать обнаруженный Harbor Gateway';
useLayoutEffect(() => {
if (previousGatewayDirectRef.current === gatewayDirect) return;
previousGatewayDirectRef.current = gatewayDirect;
setArrowTurns((turns) => turns + 0.5);
}, [gatewayDirect]);
function startModeAnimation() {
stopModeAnimationRef.current = false;
setModeAnimating(true);
@@ -517,7 +525,12 @@ function HarborBrand({ isGateway, gatewayAvailable, gatewayDirect, blocked, onSe
<em className="harbor-mode-connect">Connect</em>
<em className="harbor-mode-gateway"><span>Gateway</span></em>
</span>
<svg className="harbor-mode-swap" viewBox="0 0 18 18" aria-hidden="true">
<svg
className="harbor-mode-swap"
viewBox="0 0 18 18"
aria-hidden="true"
style={{ '--harbor-arrow-turn': `${arrowTurns}turn` }}
>
<g className="is-connect"><path d="M3 6h10m-3-3 3 3-3 3" /></g>
<g className="is-gateway"><path d="M15 12H5m3 3-3-3 3-3" /></g>
</svg>

View File

@@ -529,35 +529,20 @@ p {
stroke-width: 1.35;
transform: translateY(-50%);
transition: color 320ms ease, opacity 320ms ease, transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
animation: harbor-mode-arrows-discovered 260ms 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
.harbor-mode-swap .is-connect {
animation: harbor-mode-arrow-right-discovered 300ms 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
.harbor-mode-swap .is-gateway {
animation: harbor-mode-arrow-left-discovered 300ms 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes harbor-mode-arrow-right-discovered {
@keyframes harbor-mode-arrows-discovered {
from {
opacity: 0;
filter: blur(2px);
transform: translateX(-12px);
}
}
@keyframes harbor-mode-arrow-left-discovered {
from {
opacity: 0;
filter: blur(2px);
transform: translateX(12px);
}
}
.harbor-mode-swap g {
transform-box: view-box;
transform-origin: 9px 9px;
transform: rotate(var(--harbor-arrow-turn, 0turn));
transition: opacity 420ms ease, filter 420ms ease, transform 560ms cubic-bezier(0.16, 1, 0.3, 1);
}
@@ -591,13 +576,11 @@ p {
.harbor-brand.is-gateway-active .harbor-mode-swap .is-connect {
opacity: 0.28;
filter: blur(0.45px);
transform: rotate(180deg);
}
.harbor-brand.is-gateway-active .harbor-mode-swap .is-gateway {
opacity: 1;
filter: none;
transform: rotate(180deg);
}
.harbor-mode-tooltip {