Add seed/Telegram login button and traffic usage indicator
New non-blocking login panel on the Home screen (seed backup/restore + Telegram magic-link via the bot deep-link, polled through the new backend endpoints) — uses its own popover instead of a full-screen Dialog so the burger menu stays clickable while it's open. VpnStats now shows a used/limit usage bar backed by a new useUsageStore. Also registers tauri-plugin-shell (needed to open the t.me deep-link) and threads an optional auth token from the app down through TunnelConfig to the proxy client (desktop + Android), for instances running --require-auth. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+65
-18
@@ -1,4 +1,7 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTrafficStore } from "@/store/useTrafficStore"; // путь к твоему стору
|
||||
import { useUsageStore } from "@/store/useUsageStore";
|
||||
import { useVpnStore } from "@/store/useVpnStore";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -19,33 +22,77 @@ export function VpnStats({ className }: { className?: string }) {
|
||||
const rx = useTrafficStore((state) => state.rx);
|
||||
const tx = useTrafficStore((state) => state.tx);
|
||||
|
||||
const status = useVpnStore((state) => state.status);
|
||||
const usedBytes = useUsageStore((state) => state.usedBytes);
|
||||
const limitBytes = useUsageStore((state) => state.limitBytes);
|
||||
const startAutoRefresh = useUsageStore((state) => state.startAutoRefresh);
|
||||
const stopAutoRefresh = useUsageStore((state) => state.stopAutoRefresh);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === "connected") {
|
||||
startAutoRefresh();
|
||||
} else {
|
||||
stopAutoRefresh();
|
||||
}
|
||||
return () => stopAutoRefresh();
|
||||
}, [status, startAutoRefresh, stopAutoRefresh]);
|
||||
|
||||
const usagePercent =
|
||||
usedBytes !== null && limitBytes ? Math.min(100, (usedBytes / limitBytes) * 100) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full max-w-70 relative flex items-center justify-between px-8 py-4 rounded-2xl",
|
||||
"w-full max-w-70 relative flex flex-col gap-3 px-8 py-4 rounded-2xl",
|
||||
"bg-background/20 backdrop-blur-md border border-white/10 shadow-lg",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col items-center flex-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-muted-foreground font-bold">
|
||||
{t("rx")}
|
||||
</span>
|
||||
<span className="text-sm font-mono text-foreground font-semibold">
|
||||
{formatBytes(rx)}
|
||||
</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col items-center flex-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-muted-foreground font-bold">
|
||||
{t("rx")}
|
||||
</span>
|
||||
<span className="text-sm font-mono text-foreground font-semibold">
|
||||
{formatBytes(rx)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="h-8 w-px bg-border/50 mx-4" />
|
||||
|
||||
<div className="flex flex-col items-center flex-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-muted-foreground font-bold">
|
||||
{t("tx")}
|
||||
</span>
|
||||
<span className="text-sm font-mono text-foreground font-semibold">
|
||||
{formatBytes(tx)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-8 w-px bg-border/50 mx-4" />
|
||||
|
||||
<div className="flex flex-col items-center flex-1">
|
||||
<span className="text-[10px] uppercase tracking-widest text-muted-foreground font-bold">
|
||||
{t("tx")}
|
||||
</span>
|
||||
<span className="text-sm font-mono text-foreground font-semibold">
|
||||
{formatBytes(tx)}
|
||||
</span>
|
||||
</div>
|
||||
{usedBytes !== null && (
|
||||
<div className="flex flex-col gap-1 pt-1 border-t border-white/10">
|
||||
<div className="flex items-center justify-between text-[10px] uppercase tracking-widest text-muted-foreground font-bold">
|
||||
<span>{t("usage_label")}</span>
|
||||
<span className="font-mono normal-case tracking-normal">
|
||||
{limitBytes
|
||||
? `${formatBytes(usedBytes)} / ${formatBytes(limitBytes)}`
|
||||
: `${formatBytes(usedBytes)} / ${t("usage_unlimited")}`}
|
||||
</span>
|
||||
</div>
|
||||
{usagePercent !== null && (
|
||||
<div className="h-1.5 w-full rounded-full bg-white/10 overflow-hidden">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full rounded-full transition-all",
|
||||
usagePercent >= 90 ? "bg-destructive" : "bg-primary",
|
||||
)}
|
||||
style={{ width: `${usagePercent}%` }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user