initial commit
This commit is contained in:
156
src/App.tsx
Executable file
156
src/App.tsx
Executable file
@@ -0,0 +1,156 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Box, VStack, Button, Text, Badge, Image } from '@chakra-ui/react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { ConnectionControls } from './components/ConnectControls';
|
||||
import { DarkMode } from '@chakra-ui/system';
|
||||
// @ts-expect-error
|
||||
import shield from './icons/icon.png';
|
||||
|
||||
function App() {
|
||||
const [configName, setConfigName] = useState<string>('');
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
// Poll connection status every 3 seconds
|
||||
const { data: connectionStatus } = useQuery({
|
||||
queryKey: ['connectionStatus'],
|
||||
queryFn: async () => {
|
||||
const result = await window.electronAPI.getConnectionStatus();
|
||||
return result.success && result.connected ? result.connected : false;
|
||||
},
|
||||
refetchInterval: 3000,
|
||||
initialData: false,
|
||||
});
|
||||
|
||||
const checkCurrentConfig = async () => {
|
||||
const result = await window.electronAPI.getCurrentConfig();
|
||||
if (result.success && result.hasConfig && result.configName) {
|
||||
setConfigName(result.configName);
|
||||
}
|
||||
};
|
||||
|
||||
// Check for existing config on mount
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line
|
||||
checkCurrentConfig();
|
||||
}, []);
|
||||
|
||||
const handleLoadConfig = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const selectResult = await window.electronAPI.selectConfigFile();
|
||||
|
||||
if (!selectResult.success || !selectResult.filePath) {
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const loadResult = await window.electronAPI.loadConfig(
|
||||
selectResult.filePath
|
||||
);
|
||||
|
||||
if (loadResult.success && loadResult.configName) {
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DarkMode>
|
||||
<Box
|
||||
minH="100vh"
|
||||
bg="gray.600"
|
||||
color="gray.600"
|
||||
p={6}
|
||||
borderTop="none"
|
||||
borderWidth="1px"
|
||||
borderColor="gray.600"
|
||||
>
|
||||
<VStack gap={4} align="stretch">
|
||||
<Image h={75} w={75} margin="auto" src={shield} />
|
||||
|
||||
<Text
|
||||
color="gray.300"
|
||||
fontSize="3xl"
|
||||
fontWeight="bold"
|
||||
textAlign="center"
|
||||
>
|
||||
Dark Shield
|
||||
</Text>
|
||||
|
||||
<Box
|
||||
p={4}
|
||||
bg="gray.300"
|
||||
borderRadius="md"
|
||||
borderWidth="1px"
|
||||
borderColor="gray.600"
|
||||
shadow="sm"
|
||||
>
|
||||
<VStack gap={3} align="stretch">
|
||||
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
|
||||
Connection Status
|
||||
</Text>
|
||||
<Badge
|
||||
bg={connectionStatus ? 'green.50' : 'red.50'}
|
||||
borderRadius="md"
|
||||
borderWidth="1px"
|
||||
borderColor={connectionStatus ? 'green.300' : 'red.300'}
|
||||
color={connectionStatus ? 'green.700' : 'red.700'}
|
||||
fontSize="md"
|
||||
p={2}
|
||||
textAlign="center"
|
||||
>
|
||||
{connectionStatus ? 'Connected' : 'Disconnected'}
|
||||
</Badge>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
p={4}
|
||||
bg="gray.300"
|
||||
borderRadius="md"
|
||||
borderWidth="1px"
|
||||
borderColor="gray.600"
|
||||
shadow="sm"
|
||||
>
|
||||
<VStack gap={3} align="stretch">
|
||||
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
|
||||
Configuration
|
||||
</Text>
|
||||
{configName ? (
|
||||
<Text fontSize="md" color="gray.800" fontWeight="medium">
|
||||
{configName}
|
||||
</Text>
|
||||
) : (
|
||||
<Text fontSize="sm" color="gray.800">
|
||||
No config loaded
|
||||
</Text>
|
||||
)}
|
||||
<Button
|
||||
bg="gray.800"
|
||||
color="white"
|
||||
_hover={{ bg: 'gray.700' }}
|
||||
onClick={handleLoadConfig}
|
||||
loading={isLoading}
|
||||
loadingText="Loading..."
|
||||
>
|
||||
Load Config
|
||||
</Button>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<ConnectionControls
|
||||
isConnected={connectionStatus}
|
||||
hasConfig={!!configName}
|
||||
onConnectionChange={() => {}}
|
||||
/>
|
||||
</VStack>
|
||||
</Box>
|
||||
</DarkMode>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
94
src/components/ConnectControls.tsx
Executable file
94
src/components/ConnectControls.tsx
Executable file
@@ -0,0 +1,94 @@
|
||||
import { useState } from 'react';
|
||||
import { Box, VStack, Button, Text } from '@chakra-ui/react';
|
||||
|
||||
interface ConnectionControlsProps {
|
||||
isConnected: boolean;
|
||||
hasConfig: boolean;
|
||||
onConnectionChange: (connected: boolean) => void;
|
||||
}
|
||||
|
||||
export function ConnectionControls({
|
||||
isConnected,
|
||||
hasConfig,
|
||||
onConnectionChange,
|
||||
}: ConnectionControlsProps) {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const handleConnect = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const result = await window.electronAPI.connectWireguard();
|
||||
|
||||
if (result.success) {
|
||||
onConnectionChange(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const result = await window.electronAPI.disconnectWireguard();
|
||||
|
||||
if (result.success) {
|
||||
onConnectionChange(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
}, 2000);
|
||||
} else {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
p={4}
|
||||
bg="gray.300"
|
||||
borderRadius="md"
|
||||
borderWidth="1px"
|
||||
borderColor="gray.600"
|
||||
shadow="sm"
|
||||
>
|
||||
<VStack gap={3} align="stretch">
|
||||
<Text fontSize="sm" color="gray.800" fontWeight="semibold">
|
||||
Connection Controls
|
||||
</Text>
|
||||
{!hasConfig && (
|
||||
<Text fontSize="sm" color="orange.600">
|
||||
Please load a config file first
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{hasConfig && isConnected ? (
|
||||
<Button
|
||||
bg="red.600"
|
||||
color="white"
|
||||
_hover={{ bg: 'red.700' }}
|
||||
onClick={handleDisconnect}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
Disconnect
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
bg="green.600"
|
||||
color="white"
|
||||
_hover={{ bg: 'green.700' }}
|
||||
onClick={handleConnect}
|
||||
disabled={isLoading}
|
||||
loading={isLoading}
|
||||
>
|
||||
Connect
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
BIN
src/icons/icon.png
Executable file
BIN
src/icons/icon.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
21
src/main.tsx
Executable file
21
src/main.tsx
Executable file
@@ -0,0 +1,21 @@
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import App from './App';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
retry: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<ChakraProvider value={defaultSystem}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
</QueryClientProvider>
|
||||
</ChakraProvider>
|
||||
);
|
||||
52
src/vite.env.d.ts
vendored
Executable file
52
src/vite.env.d.ts
vendored
Executable file
@@ -0,0 +1,52 @@
|
||||
declare module '*.png' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
export interface ElectronAPI {
|
||||
getConnectionStatus: () => Promise<{
|
||||
success: boolean;
|
||||
connected?: boolean;
|
||||
}>;
|
||||
getCurrentConfig: () => Promise<{
|
||||
success: boolean;
|
||||
hasConfig?: boolean;
|
||||
configName?: string;
|
||||
}>;
|
||||
selectConfigFile: () => Promise<{
|
||||
success: boolean;
|
||||
filePath?: string;
|
||||
}>;
|
||||
loadConfig: (filePath: string) => Promise<{
|
||||
success: boolean;
|
||||
configName?: string;
|
||||
}>;
|
||||
connectWireguard: () => Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
disconnectWireguard: () => Promise<{
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
electronAPI: ElectronAPI;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
Reference in New Issue
Block a user