Skip to content

Commit

Permalink
show own address, allow manual address
Browse files Browse the repository at this point in the history
  • Loading branch information
jmlee337 committed Jan 29, 2025
1 parent eda7f6c commit 13b813a
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 35 deletions.
81 changes: 60 additions & 21 deletions src/main/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ import {
WebSocketServerStatus,
} from '../common/types';

const PORT = 52455;

type HostMessage = {
type: 'name';
name: string;
};

type HostRequest = {
ordinal: number;
} & (
Expand Down Expand Up @@ -56,8 +63,6 @@ type HostResponse = {
error: string;
};

const PORT = 52455;

function getComputerName() {
switch (process.platform) {
case 'win32':
Expand Down Expand Up @@ -141,10 +146,7 @@ let address = '';
let name = '';
let webSocket: WebSocket | null = null;
export function connectToHost(newAddress: string) {
const newName = addressToName.get(newAddress);
if (!newName) {
throw new Error('address not known');
}
const newName = addressToName.get(newAddress) ?? '';

if (webSocket) {
webSocket.onclose = () => {};
Expand All @@ -153,25 +155,49 @@ export function connectToHost(newAddress: string) {
name = '';
}

webSocket = new WebSocket(`ws://${newAddress}:${PORT}`);
webSocket.onopen = () => {
address = newAddress;
name = newName;
mainWindow?.webContents.send('copyHost', { address, name });
webSocket = new WebSocket(`ws://${newAddress}:${PORT}`, {
handshakeTimeout: 1000,
});
webSocket.addEventListener('message', (event) => {
if (typeof event.data !== 'string') {
return;
}

const request: HostRequest = {
ordinal: 0,
request: 'computerName',
name: getComputerName(),
};
webSocket?.send(JSON.stringify(request));
};
const message = JSON.parse(event.data) as HostMessage;
if (message.type === 'name') {
name = message.name;
mainWindow?.webContents.send('copyHost', { address, name });
}
});
webSocket.onclose = () => {
address = '';
name = '';
mainWindow?.webContents.send('copyHost', { address, name });
webSocket = null;
};
return new Promise<void>((resolve, reject) => {
if (!webSocket) {
reject();
return;
}

webSocket.onopen = () => {
address = newAddress;
name = newName;
mainWindow?.webContents.send('copyHost', { address, name });

const request: HostRequest = {
ordinal: 0,
request: 'computerName',
name: getComputerName(),
};
webSocket?.send(JSON.stringify(request));
resolve();
};
webSocket.onerror = (event) => {
reject(event.error);
};
});
}

export function disconnectFromHost() {
Expand Down Expand Up @@ -438,6 +464,14 @@ function sendCopyClients() {
),
);
}
export function getCopyClients() {
return Array.from(clientAddressToNameAndWebSocket).map(
([clientAddress, { name: clientName }]): CopyRemote => ({
address: clientAddress,
name: clientName,
}),
);
}

let webSocketServer: WebSocketServer | null = null;
const subdirToWriteDir = new Map<string, string>();
Expand Down Expand Up @@ -743,18 +777,23 @@ let broadcastSocket: Socket | null = null;
let timeout: NodeJS.Timeout | null = null;
export function startBroadcasting() {
if (broadcastSocket) {
return Promise.resolve();
return Promise.resolve(broadcastSocket.address().address);
}
if (!webSocketServer) {
return Promise.reject(new Error('must start hostServer first'));
}

return new Promise<void>((resolve, reject) => {
return new Promise<string>((resolve, reject) => {
broadcastSocket = createSocket('udp4');
broadcastSocket.on('error', (e) => {
reject(e);
});
broadcastSocket.on('connect', () => {
if (!broadcastSocket) {
reject();
return;
}

const selfName = getComputerName();
const broadcast = () => {
if (broadcastSocket) {
Expand All @@ -765,7 +804,7 @@ export function startBroadcasting() {
}
};
broadcast();
resolve();
resolve(broadcastSocket.address().address);
});
broadcastSocket.connect(PORT, '255.255.255.255');
});
Expand Down
4 changes: 4 additions & 0 deletions src/main/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
import {
connectToHost,
disconnectFromHost,
getCopyClients,
getHost,
setCopyDir,
setMainWindow,
Expand Down Expand Up @@ -252,6 +253,9 @@ export default function setupIPCs(mainWindow: BrowserWindow): void {
ipcMain.removeHandler('disconnectFromHost');
ipcMain.handle('disconnectFromHost', () => disconnectFromHost);

ipcMain.removeHandler('getCopyClients');
ipcMain.handle('getCopyClients', getCopyClients);

ipcMain.removeHandler('startHostServer');
ipcMain.handle('startHostServer', startHostServer);

Expand Down
4 changes: 3 additions & 1 deletion src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ const electronHandler = {
ipcRenderer.removeAllListeners('copyHost');
ipcRenderer.on('copyHost', callback);
},
getCopyClients: (): Promise<CopyRemote[]> =>
ipcRenderer.invoke('getCopyClients'),
startHostServer: (): Promise<string> => ipcRenderer.invoke('startHostServer'),
stopHostServer: (): Promise<void> => ipcRenderer.invoke('stopHostServer'),
startBroadcastingHost: (): Promise<void> =>
startBroadcastingHost: (): Promise<string> =>
ipcRenderer.invoke('startBroadcastingHost'),
stopBroadcastingHost: (): Promise<void> =>
ipcRenderer.invoke('stopBroadcastingHost'),
Expand Down
91 changes: 78 additions & 13 deletions src/renderer/CopyControls.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BrowserUpdated, Close, FolderOpen, Router } from '@mui/icons-material';
import {
Alert,
Button,
CircularProgress,
Dialog,
Expand Down Expand Up @@ -39,7 +40,15 @@ function ClientsDialog({
const [open, setOpen] = useState(false);
const [status, setStatus] = useState(WebSocketServerStatus.STOPPED);
const [clients, setClients] = useState<CopyRemote[]>([]);
const [selfAddress, setSelfAddress] = useState('');
const [selfName, setSelfName] = useState('');

useEffect(() => {
(async () => {
setClients(await window.electron.getCopyClients());
})();
}, []);

useEffect(() => {
window.electron.onHostServerStatus((event, newStatus) => {
setStatus(newStatus);
Expand All @@ -59,7 +68,7 @@ function ClientsDialog({
endIcon={<BrowserUpdated />}
onClick={async () => {
setSelfName(await window.electron.startHostServer());
await window.electron.startBroadcastingHost();
setSelfAddress(await window.electron.startBroadcastingHost());
setOpen(true);
}}
>
Expand All @@ -80,7 +89,9 @@ function ClientsDialog({
>
<DialogTitle>Hosting on LAN...</DialogTitle>
<DialogContent>
<DialogContentText>Hosting as {selfName}</DialogContentText>
<DialogContentText>
Hosting at {selfAddress} - {selfName}
</DialogContentText>
<List>
{clients.map(({ address, name }) => (
<ListItem
Expand All @@ -107,13 +118,7 @@ function ClientsDialog({
);
}

function HostsDialog({
host,
setHost,
}: {
host: CopyRemote;
setHost: (host: CopyRemote) => void;
}) {
function HostsDialog({ host }: { host: CopyRemote }) {
const [open, setOpen] = useState(false);
const [hosts, setHosts] = useState<CopyRemote[]>([]);
const [selfName, setSelfName] = useState('');
Expand All @@ -123,12 +128,17 @@ function HostsDialog({
});
}, []);

const [connecting, setConnecting] = useState(false);
const [manualAddress, setManualAddress] = useState('');
const [error, setError] = useState('');

return (
<>
<Tooltip title="Search LAN">
<Button
onClick={async () => {
setHosts([]);
setError('');
setSelfName(await window.electron.startListeningForHosts());
setOpen(true);
}}
Expand All @@ -147,6 +157,53 @@ function HostsDialog({
>
<DialogTitle>Searching LAN for host Replay Manager...</DialogTitle>
<DialogContent>
<form
style={{
alignItems: 'center',
display: 'flex',
marginTop: '8px',
marginBottom: '8px',
gap: '8px',
}}
onSubmit={async (event) => {
event.preventDefault();
event.stopPropagation();

setConnecting(true);
await window.electron.stopListeningForHosts();
try {
await window.electron.connectToHost(manualAddress);
setOpen(false);
} catch (e: unknown) {
if (e instanceof Error) {
setError(e.message);
}
} finally {
setConnecting(false);
}
}}
>
<TextField
autoFocus
label="IP Address"
placeholder="192.168.0.106"
size="small"
variant="outlined"
value={manualAddress}
onChange={(event) => {
setManualAddress(event.target.value);
}}
/>
<Button
disabled={connecting}
endIcon={connecting && <CircularProgress size="24px" />}
type="submit"
variant="contained"
>
Connect
</Button>
</form>
{error && <Alert severity="error">{error}</Alert>}
<DialogContentText>Searching as {selfName}</DialogContentText>
<List>
{host.address && host.name && (
Expand Down Expand Up @@ -174,10 +231,18 @@ function HostsDialog({
>
<ListItemButton
onClick={async () => {
await window.electron.connectToHost(address);
setHost({ address, name });
setConnecting(true);
await window.electron.stopListeningForHosts();
setOpen(false);
try {
await window.electron.connectToHost(address);
setOpen(false);
} catch (e: unknown) {
if (e instanceof Error) {
setError(e.message);
}
} finally {
setConnecting(false);
}
}}
>
<ListItemText>
Expand Down Expand Up @@ -273,7 +338,7 @@ export default function CopyControls({
{!(host.address && host.name) && (
<ClientsDialog disabled={!dir} setHosting={setHosting} />
)}
{!hosting && <HostsDialog host={host} setHost={setHost} />}
{!hosting && <HostsDialog host={host} />}
</>
)}
<Tooltip title="Set copy folder">
Expand Down

0 comments on commit 13b813a

Please sign in to comment.