network-interface-viewer.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
  2. import { useTranslation } from "react-i18next";
  3. import { BaseDialog, DialogRef, Notice } from "@/components/base";
  4. import { getNetworkInterfacesInfo } from "@/services/cmds";
  5. import { alpha, Box, Button, Chip, IconButton } from "@mui/material";
  6. import { ContentCopyRounded } from "@mui/icons-material";
  7. import { writeText } from "@tauri-apps/api/clipboard";
  8. export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
  9. const { t } = useTranslation();
  10. const [open, setOpen] = useState(false);
  11. const [networkInterfaces, setNetworkInterfaces] = useState<
  12. INetworkInterface[]
  13. >([]);
  14. const [isV4, setIsV4] = useState(true);
  15. useImperativeHandle(ref, () => ({
  16. open: () => {
  17. setOpen(true);
  18. },
  19. close: () => setOpen(false),
  20. }));
  21. useEffect(() => {
  22. if (!open) return;
  23. getNetworkInterfacesInfo().then((res) => {
  24. console.log(res);
  25. setNetworkInterfaces(res);
  26. });
  27. }, [open]);
  28. return (
  29. <BaseDialog
  30. open={open}
  31. title={
  32. <Box display="flex" justifyContent="space-between">
  33. {t("Network Interface")}
  34. <Box>
  35. <Button
  36. variant="contained"
  37. size="small"
  38. onClick={() => {
  39. setIsV4((prev) => !prev);
  40. }}
  41. >
  42. {isV4 ? t("Ipv6") : t("Ipv4")}
  43. </Button>
  44. </Box>
  45. </Box>
  46. }
  47. contentSx={{ width: 450, maxHeight: 330 }}
  48. okBtn={t("Save")}
  49. cancelBtn={t("Cancel")}
  50. onClose={() => setOpen(false)}
  51. onCancel={() => setOpen(false)}
  52. >
  53. {networkInterfaces.map((item) => (
  54. <Box key={item.name}>
  55. <h4>{item.name}</h4>
  56. <Box>
  57. {isV4 && (
  58. <>
  59. {item.addr.map(
  60. (address) =>
  61. address.V4 && (
  62. <AddressDisplay
  63. key={address.V4.ip}
  64. label="Address"
  65. content={address.V4.ip}
  66. />
  67. )
  68. )}
  69. <AddressDisplay label="Mac" content={item.mac_addr ?? ""} />
  70. </>
  71. )}
  72. {!isV4 && (
  73. <>
  74. {item.addr.map(
  75. (address) =>
  76. address.V6 && (
  77. <AddressDisplay
  78. key={address.V6.ip}
  79. label="Address"
  80. content={address.V6.ip}
  81. />
  82. )
  83. )}
  84. <AddressDisplay label="Mac" content={item.mac_addr ?? ""} />
  85. </>
  86. )}
  87. </Box>
  88. </Box>
  89. ))}
  90. </BaseDialog>
  91. );
  92. });
  93. const AddressDisplay = (props: { label: string; content: string }) => {
  94. const { t } = useTranslation();
  95. return (
  96. <Box
  97. sx={{
  98. display: "flex",
  99. justifyContent: "space-between",
  100. margin: "8px 0",
  101. }}
  102. >
  103. <Box>{props.label}</Box>
  104. <Box
  105. sx={({ palette }) => ({
  106. borderRadius: "8px",
  107. padding: "2px",
  108. background:
  109. palette.mode === "dark"
  110. ? alpha(palette.background.paper, 0.3)
  111. : alpha(palette.grey[400], 0.3),
  112. })}
  113. >
  114. <Box sx={{ display: "inline", userSelect: "text" }}>
  115. {props.content}
  116. </Box>
  117. <IconButton
  118. size="small"
  119. onClick={async () => {
  120. await writeText(props.content);
  121. Notice.success(t("Copy Success"));
  122. }}
  123. >
  124. <ContentCopyRounded sx={{ fontSize: "18px" }} />
  125. </IconButton>
  126. </Box>
  127. </Box>
  128. );
  129. };