import React, { useState } from "react"; import { useHistory } from "react-router"; import { useDispatch, useSelector } from "react-redux"; import { Button, Flex, Input, Textarea, Text } from "@chakra-ui/react"; import { toast } from "react-toastify"; import { addFile } from "../store/modules/wgConfig/action"; import { StoreState, AppState, WgConfigState } from "../types/store"; import Content from "../components/Content"; export default function NewTunnel() { const history = useHistory(); const dispatch = useDispatch(); const [hiddenInput, setHiddenInput] = useState<HTMLInputElement | null>(); const [fileName, setFileName] = useState<string>(); const [interfaceText, setInterfaceText] = useState<string>(); const { userDataPath } = useSelector<StoreState, AppState>( (state) => state.app ); const { files } = useSelector<StoreState, WgConfigState>( (state) => state.wgConfig ); function handleImport(event: React.ChangeEvent<HTMLInputElement>) { if (event.currentTarget.files && event.currentTarget.files.length > 0) { const currentFile = event.currentTarget.files[0]; try { const reader = new FileReader(); reader.readAsText(currentFile, "utf-8"); reader.onload = function (evt: ProgressEvent<FileReader>) { if (!evt.target) return; const data = evt.target.result as string; const name = currentFile.name.split(".conf"); setFileName(name[0]); setInterfaceText(data); }; reader.onerror = function () { toast(`Could not read file ${currentFile.name}`, { type: "error" }); }; } catch (e) { toast(e.message, { type: "error" }); } } } function handleCancel() { history.push("/"); } async function handleSave() { if (!fileName || fileName.length === 0) { toast("Name cannot be empty", { type: "error" }); return; } if (fileName.length > 15) { toast("Filename is too long, maximum 15 characters", { type: "error" }); return; } if (!interfaceText || interfaceText.length === 0) { toast("Interface cannot be empty", { type: "error" }); return; } if (files.some(f => f.name === fileName)) { toast(`A tunnel named ${fileName} already exists`, { type: "error" }); return; } try { dispatch(addFile(`${fileName}.conf`, interfaceText, userDataPath)); history.push(`/tunnel/${fileName}`); } catch (e) { toast(e.message, { type: "error" }); } } function onNameChange(event: React.ChangeEvent<HTMLInputElement>) { setFileName(event.target.value); } function onInterfaceTextChange(event: React.ChangeEvent<HTMLTextAreaElement>) { setInterfaceText(event.target.value); } return ( <Content> <Flex bg="gray.200" borderRadius="4" color="whiteAlpha.700" direction="column" p="4" w="575px" h="auto" mx="auto" my="10" > <Flex justify="space-between" w="100%"> <Text color="whiteAlpha.800" fontSize="lg" fontWeight="bold"> New Tunnel </Text> <Button size="xs" onClick={() => hiddenInput?.click()}> Import </Button> <input hidden type="file" accept=".conf" onChange={handleImport} ref={(el) => setHiddenInput(el)} ></input> </Flex> <Flex align="center" mt="4" w="100%"> <Text>Name:</Text> <Input bg="gray.300" borderColor="transparent" size="xs" w="50%" ml="2" value={fileName || ""} onChange={onNameChange} /> </Flex> <Flex direction="column" mt="4" w="100%" h="100%"> <Text>Interface:</Text> <Textarea bg="gray.300" borderColor="transparent" size="xs" resize="none" mt="2" w="100%" h="100%" value={interfaceText || ""} onChange={onInterfaceTextChange} /> </Flex> <Flex justify="flex-end" mt="4"> <Button size="sm" onClick={handleCancel}> Cancel </Button> <Button colorScheme="orange" color="whiteAlpha.800" size="sm" ml="4" disabled={!fileName || !interfaceText} onClick={handleSave} > Save </Button> </Flex> </Flex> </Content> ); }