network-switcher.tsx 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. "use client";
  2. import { useEffect, useState } from "react";
  3. import {
  4. getActiveNetwork,
  5. listMaviNetworks,
  6. readNetworkId,
  7. setNetworkId,
  8. type MaviNetworkId,
  9. } from "@/lib/mavi/network";
  10. import { cn } from "@/lib/utils";
  11. export function NetworkSwitcher({
  12. className,
  13. onChange,
  14. }: {
  15. className?: string;
  16. onChange?: (id: MaviNetworkId) => void;
  17. }) {
  18. const [id, setId] = useState<MaviNetworkId>("private-anvil");
  19. const [mounted, setMounted] = useState(false);
  20. useEffect(() => {
  21. setMounted(true);
  22. setId(readNetworkId());
  23. const sync = () => setId(readNetworkId());
  24. window.addEventListener("mavi-network", sync);
  25. return () => window.removeEventListener("mavi-network", sync);
  26. }, []);
  27. if (!mounted) {
  28. return (
  29. <div className={cn("h-10 animate-pulse rounded-lg bg-muted/40", className)} />
  30. );
  31. }
  32. const active = getActiveNetwork();
  33. return (
  34. <div className={cn("space-y-2", className)}>
  35. <label htmlFor="mavi-network" className="text-sm font-medium">
  36. Network
  37. </label>
  38. <select
  39. id="mavi-network"
  40. value={id}
  41. onChange={(e) => {
  42. const next = e.target.value as MaviNetworkId;
  43. setNetworkId(next);
  44. setId(next);
  45. onChange?.(next);
  46. }}
  47. className="flex h-10 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
  48. >
  49. {listMaviNetworks().map((n) => (
  50. <option key={n.id} value={n.id}>
  51. {n.label}
  52. </option>
  53. ))}
  54. </select>
  55. <p className="text-[11px] text-muted-foreground leading-snug">
  56. {active.hint} · chain id {active.chainId}
  57. </p>
  58. </div>
  59. );
  60. }