button update

This commit is contained in:
2026-03-31 14:39:04 +07:00
parent 0f7f01310e
commit bd51ac5ddf
8 changed files with 582 additions and 149 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import { Moon, Sun } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useTheme } from "@/hooks/useTheme";
import { useTheme } from "@/ThemeProvider";
export function ThemeToggle() {
const { isDark, toggleDark } = useTheme();
+22 -79
View File
@@ -1,14 +1,22 @@
import * as React from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Power } from "lucide-react";
import { cn } from "@/lib/utils";
import { useTranslation } from "react-i18next";
import { TunnelConfig, startVpn, stopVpn } from "vpn-plugin";
import { useVpnStore } from "@/store/useVpnStore";
import { useEffect } from "react";
import { listen } from "@tauri-apps/api/event";
import VpnControlButton from "@/components/shared/VpnControlButton";
import { useTheme } from "@/ThemeProvider";
export function VpnControl() {
const { status, setStatus } = useVpnStore();
const { t } = useTranslation();
// Подключаем тему
const { isDark } = useTheme();
const currentTheme = isDark ? "dark" : "light";
const handleToggle = async () => {
try {
if (status === "idle") {
@@ -30,20 +38,16 @@ export function VpnControl() {
}
};
// Слушаем события напрямую в компоненте
useEffect(() => {
let unlisten: (() => void) | null = null;
async function setupListener() {
// Слушаем глобальное событие 'status-change', которое шлет Rust через app.emit
unlisten = await listen<{ status: string }>("status-change", (event) => {
console.log("--- [GUI] Direct Event Received:", event.payload);
const newStatus = event.payload.status as
| "idle"
| "connecting"
| "connected";
// Обновляем глобальный стор
if (newStatus) {
setStatus(newStatus);
}
@@ -51,15 +55,11 @@ export function VpnControl() {
}
setupListener();
// Очистка при размонтировании
return () => {
if (unlisten) unlisten();
};
}, [setStatus]);
const { t } = useTranslation();
const isActive = status !== "idle";
const statusMap = {
@@ -70,77 +70,19 @@ export function VpnControl() {
return (
<div className="flex flex-col items-center justify-center gap-6 p-10">
{/* Контейнер с эффектом жидкого стекла */}
<div className="relative flex size-64 items-center justify-center rounded-full bg-background/20 backdrop-blur-xl border border-white/10 shadow-2xl p-4">
<svg
className="absolute size-full rotate-90 pointer-events-none"
viewBox="-20 -20 300 300"
>
{/* Фоновая линия */}
<circle
cx="130"
cy="130"
r="140"
className="stroke-primary/20 fill-none"
strokeWidth="4"
/>
{/* Правая половина (по часовой от 6 к 12) */}
<motion.circle
cx="130"
cy="130"
r="140"
className="stroke-primary/80 fill-none drop-shadow-[0_0_3px_var(--color-primary)]"
strokeWidth="4"
strokeLinecap="round"
strokeDasharray="800"
initial={{ pathLength: 0 }}
animate={{
pathLength: isActive ? 0.495 : 0,
opacity: isActive ? 1 : 0,
}}
transition={{ duration: 1.5, ease: "easeInOut" }}
/>
{/* Левая половина (против часовой от 6 к 12) */}
<motion.circle
cx="130"
cy="130"
r="140"
className="stroke-primary/80 fill-none drop-shadow-[0_0_3px_var(--color-primary)]"
strokeWidth="4"
strokeLinecap="round"
strokeDasharray="800"
// Поворачиваем на 180 градусов относительно центра, чтобы рисовать в другую сторону
style={{ rotate: 180, originX: "140px", originY: "141px" }}
initial={{ pathLength: 0 }}
animate={{
pathLength: isActive ? 0.495 : 0,
opacity: isActive ? 1 : 0,
}}
transition={{ duration: 1.5, ease: "easeInOut" }}
/>
</svg>
<button
<div className="relative flex items-center justify-center size-100">
<VpnControlButton
// Теперь прокидываем реальную тему
theme={currentTheme}
isOn={isActive}
onClick={handleToggle}
disabled={status === "connecting"}
className={cn(
"relative size-40 rounded-full transition-all duration-700 flex items-center justify-center",
isActive
? "bg-primary/20 shadow-[0_0_40px_var(--color-primary)]"
: "bg-muted shadow-inner",
)}
>
<Power
className={cn(
"size-16 transition-colors duration-500",
isActive ? "text-primary" : "text-muted-foreground",
)}
/>
</button>
style={{
pointerEvents: status === "connecting" ? "none" : "auto",
opacity: status === "connecting" ? 0.8 : 1,
}}
/>
</div>
{/* Статус */}
<div className="text-center space-y-1">
<p className="text-sm uppercase tracking-widest text-muted-foreground font-semibold">
{t("status_label")}
@@ -152,8 +94,9 @@ export function VpnControl() {
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
className={cn(
"text-xl font-bold",
isActive ? "text-primary" : "text-foreground",
"text-xl font-bold tracking-tight",
status === "connected" ? "text-primary" : "text-foreground",
status === "connecting" && "animate-pulse opacity-70",
)}
>
{statusMap[status]}