diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2d91894..2e77ef9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,4 +30,4 @@ jobs: npm run make npm run publish env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/app.tsx b/src/app.tsx index 8f5a01b..1d6d828 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -17,13 +17,22 @@ export default function App() { const [port, setPort] = useState(10747) const [connected, setConnected] = useState(false) const [connecting, setConnecting] = useState(false) - const [viewState, setViewState] = useState({ + const [aircraftPosition, setAircraftPosition] = useState({ bearing: 0, longitude: 0.051536548427520756, latitude: 51.50527121507392, }); + useEffect(() => { + const getSavedConnection = async () => { + const savedHost = await window.api.getHost(); + if (savedHost) setHost(savedHost); + const savedPort = await window.api.getPort(); + if (savedPort) setPort(savedPort); + connectAerowinx(); + } + window.aerowinxApi.onConnected(() => { console.log('Connected to Aerowinx'); setConnected(true); @@ -49,11 +58,11 @@ export default function App() { }); window.aerowinxApi.onQs121((data: Qs121) => { - if(Object.values(data).some(v => Number.isNaN(v))) return; - setViewState({...viewState, longitude: data.lon, latitude: data.lat, bearing: data.heading}) + if (Object.values(data).some(v => Number.isNaN(v))) return; + setAircraftPosition({ ...aircraftPosition, longitude: data.lon, latitude: data.lat, bearing: data.heading }) }); - connectAerowinx(); + getSavedConnection(); return () => { window.aerowinxApi.removeListeners(); @@ -62,7 +71,7 @@ export default function App() { }, []) const connectAerowinx = () => { - window.aerowinxApi.connect({host, port}) + window.aerowinxApi.connect({ host, port }) setConnecting(true); } @@ -74,8 +83,7 @@ export default function App() { return ( <> + /> ) } \ No newline at end of file diff --git a/src/components/Map.tsx b/src/components/Map.tsx index 9308b56..167ed8f 100644 --- a/src/components/Map.tsx +++ b/src/components/Map.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Map, Marker, ScaleControl, NavigationControl, FullscreenControl, ViewState } from 'react-map-gl/maplibre'; import { Button } from 'flowbite-react'; import 'maplibre-gl/dist/maplibre-gl.css'; @@ -14,30 +14,42 @@ type AeromapViewState = { } export default function AeroMap( - { viewState, setViewState, defaultZoom, host, port, setHost, setPort, connecting, setConnecting, connected, connectAerowinx, disconnectAerowinx }: - { - viewState: AeromapViewState, - setViewState: (viewState: React.SetStateAction) => void, - defaultZoom: number, - host: string, - port: number, - setHost: (host: React.SetStateAction) => void, - setPort: (port: React.SetStateAction) => void, - connecting: boolean, - setConnecting: (isConnecting: React.SetStateAction) => void, - connected: boolean, - connectAerowinx: () => void, - disconnectAerowinx: () => void - } + { aircraftPosition, defaultZoom, host, port, setHost, setPort, connecting, setConnecting, connected, connectAerowinx, disconnectAerowinx }: + { + aircraftPosition: AeromapViewState, + defaultZoom: number, + host: string, + port: number, + setHost: (host: React.SetStateAction) => void, + setPort: (port: React.SetStateAction) => void, + connecting: boolean, + setConnecting: (isConnecting: React.SetStateAction) => void, + connected: boolean, + connectAerowinx: () => void, + disconnectAerowinx: () => void + } ) { const [mapStyle, setMapStyle] = useState('openstreetmap'); + const [viewState, setViewState] = useState(aircraftPosition); const [zoom, setZoom] = useState(defaultZoom); + const [userControl, setUserControl] = useState(false); + + useEffect(() => { + if (userControl) return; + setViewState(aircraftPosition); + }, [aircraftPosition]); + + function backToCenter() { + setViewState(aircraftPosition); + setUserControl(false); + } return ( { + setUserControl(true); setViewState((prevState: ViewState) => ({ ...prevState, longitude: evt.viewState.longitude, @@ -65,11 +77,14 @@ export default function AeroMap( connectAerowinx={connectAerowinx} disconnectAerowinx={disconnectAerowinx} /> + {userControl && ( + + )} - + diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 18667cc..32a6bce 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -28,11 +28,13 @@ export function Settings( const onChangeHost = (event: React.ChangeEvent) => { const nextHost = event.target.value; setHost(nextHost); + window.api.setHost(nextHost); } const onChangePort = (event: React.ChangeEvent) => { const nextPort = Number(event.target.value); setPort(nextPort); + window.api.setPort(nextPort); } const onChangeAutoConnect = () => { diff --git a/src/lib/host-settings.js b/src/lib/host-settings.js deleted file mode 100644 index a9b8240..0000000 --- a/src/lib/host-settings.js +++ /dev/null @@ -1,36 +0,0 @@ -import { ipcMain } from 'electron'; -import settings from 'electron-settings'; - -export const hostSettings = async () => { - let host; - let port; - - if (await settings.has('host')) { - host = await settings.get('host'); - } else { - settings.set('host', '1278.0.0.1'); - } - - if (await settings.has('port')) { - port = await settings.get('port'); - } else { - settings.set('port', '10747'); - } - - const setHost = async (state) => { - await settings.set('host', state); - host = state; - }; - - const setPort = async (state) => { - await settings.set('port', state); - port = state; - }; - - return { - host, - port, - setHost, - setPort, - }; -}; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index bb11bfd..49917aa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import path from 'path'; import { createAerowinxConnection } from './lib/aerowinx-connection'; import { windowStateKeeper } from './lib/window-size'; import { alwaysOnTopStateKeeper } from './lib/always-on-top'; -import { hostSettings } from './lib/host-settings'; +import settings from 'electron-settings'; // Handle creating/removing shortcuts on Windows when installing/uninstalling. if (require('electron-squirrel-startup')) { @@ -13,7 +13,6 @@ if (require('electron-squirrel-startup')) { const createWindow = async () => { const mainWindowStateKeeper = await windowStateKeeper('main'); const mainAlwaysOnTopStateKeeper = await alwaysOnTopStateKeeper('main'); - const mainHostSettings = await hostSettings(); // Create the browser window. const mainWindow = new BrowserWindow({ @@ -40,7 +39,11 @@ const createWindow = async () => { const setAlwaysOnTop = (event: IpcMainEvent, state: boolean) => { mainAlwaysOnTopStateKeeper.setAlwaysOnTopState(state); - mainWindow.setAlwaysOnTop(state); + if(state) { + mainWindow.setAlwaysOnTop(state, 'screen-saver'); + } else { + mainWindow.setAlwaysOnTop(state, 'normal'); + } } ipcMain.on('always-on-top:set', setAlwaysOnTop); @@ -48,31 +51,11 @@ const createWindow = async () => { return mainAlwaysOnTopStateKeeper.alwaysOnTopState; }); - ipcMain.handle('host:get', async () => { - return mainHostSettings.host; - }); - ipcMain.handle('port:get', async () => { - return mainHostSettings.port; - }); - - const setHost = (event: IpcMainEvent, arg: string) => { - mainHostSettings.setHost(arg); - } - ipcMain.on('host:set', setHost); - const setPort = (event: IpcMainEvent, arg: string) => { - mainHostSettings.setPort(arg); - } - ipcMain.on('port:set', setPort); - createAerowinxConnection(mainWindow); mainWindow.on('close', function () { ipcMain.removeHandler('always-on-top:get'); ipcMain.removeListener('always-on-top:set', setAlwaysOnTop); - ipcMain.removeHandler('host:get'); - ipcMain.removeListener('host:set', setHost); - ipcMain.removeHandler('port:get'); - ipcMain.removeListener('port:set', setPort); }); return mainWindow; @@ -104,4 +87,15 @@ app.on('activate', () => { // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and import them here. - +ipcMain.handle('host:set', async (event, data) => { + await settings.set('host', data); +}); +ipcMain.handle('host:get', async () => { + return await settings.get('host'); +}); +ipcMain.handle('port:set', async (event, data) => { + await settings.set('port', data); +}); +ipcMain.handle('port:get', async () => { + return await settings.get('port'); +}); \ No newline at end of file diff --git a/src/preload.ts b/src/preload.ts index 20d8d06..6a91a34 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -6,6 +6,10 @@ import { contextBridge, ipcRenderer } from 'electron'; contextBridge.exposeInMainWorld('api', { setAlwaysOnTop: (alwaysOnTop: boolean) => ipcRenderer.send('always-on-top:set', alwaysOnTop), getAlwaysOnTop: () => ipcRenderer.invoke('always-on-top:get'), + setHost: (host: string) => ipcRenderer.invoke('host:set', host), + getHost: async (): Promise => ipcRenderer.invoke('host:get'), + setPort: (port: number) => ipcRenderer.invoke('port:set', port), + getPort: async (): Promise => ipcRenderer.invoke('port:get'), }) contextBridge.exposeInMainWorld('aerowinxApi', { diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 0cc19ec..18fcfde 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -5,6 +5,10 @@ declare global { api: { setAlwaysOnTop: (alwaysOnTop: boolean) => void, getAlwaysOnTop: () => Promise, + setHost: (host: string) => void, + getHost: () => Promise, + setPort: (port: number) => void, + getPort: () => Promise, }, aerowinxApi: { connect: ({ host: string, port: number }) => void,