Makes socket messages event typed

This commit is contained in:
Rune Harlyk
2025-07-11 18:59:07 +02:00
parent c5901c65b3
commit 98f3fc674b
9 changed files with 382 additions and 359 deletions
+131 -126
View File
@@ -1,19 +1,24 @@
<script lang="ts">
import { onMount, onDestroy } from 'svelte';
import { modals } from 'svelte-modals';
import { slide } from 'svelte/transition';
import { cubicOut } from 'svelte/easing';
import { notifications } from '$lib/components/toasts/notifications';
import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list';
import SettingsCard from '$lib/components/SettingsCard.svelte';
import { PasswordInput } from '$lib/components/input';
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte';
import ScanNetworks from './Scan.svelte';
import Spinner from '$lib/components/Spinner.svelte';
import InfoDialog from '$lib/components/InfoDialog.svelte';
import type { KnownNetworkItem, WifiSettings, WifiStatus } from '$lib/types/models';
import { socket } from '$lib/stores';
import { api } from '$lib/api';
import { onMount, onDestroy } from 'svelte'
import { modals } from 'svelte-modals'
import { slide } from 'svelte/transition'
import { cubicOut } from 'svelte/easing'
import { notifications } from '$lib/components/toasts/notifications'
import DragDropList, { VerticalDropZone, reorder, type DropEvent } from 'svelte-dnd-list'
import SettingsCard from '$lib/components/SettingsCard.svelte'
import { PasswordInput } from '$lib/components/input'
import ConfirmDialog from '$lib/components/ConfirmDialog.svelte'
import ScanNetworks from './Scan.svelte'
import Spinner from '$lib/components/Spinner.svelte'
import InfoDialog from '$lib/components/InfoDialog.svelte'
import {
Topics,
type KnownNetworkItem,
type WifiSettings,
type WifiStatus
} from '$lib/types/models'
import { socket } from '$lib/stores'
import { api } from '$lib/api'
import {
Cancel,
Delete,
@@ -31,9 +36,9 @@
DNS,
Add,
Scan,
Edit,
} from '$lib/components/icons';
import StatusItem from '$lib/components/StatusItem.svelte';
Edit
} from '$lib/components/icons'
import StatusItem from '$lib/components/StatusItem.svelte'
let networkEditable: KnownNetworkItem = $state({
ssid: '',
@@ -43,22 +48,22 @@
subnet_mask: undefined,
gateway_ip: undefined,
dns_ip_1: undefined,
dns_ip_2: undefined,
});
dns_ip_2: undefined
})
let static_ip_config = $state(false);
let static_ip_config = $state(false)
let newNetwork: boolean = $state(true);
let showNetworkEditor: boolean = $state(false);
let newNetwork: boolean = $state(true)
let showNetworkEditor: boolean = $state(false)
let wifiStatus: WifiStatus | null = $state(null);
let wifiSettings: WifiSettings | null = $state(null);
let wifiStatus: WifiStatus | null = $state(null)
let wifiSettings: WifiSettings | null = $state(null)
let dndNetworkList: KnownNetworkItem[] = $state([]);
let dndNetworkList: KnownNetworkItem[] = $state([])
let showWifiDetails = $state(false);
let showWifiDetails = $state(false)
let formField: any = $state();
let formField: any = $state()
let formErrors = $state({
ssid: false,
@@ -66,157 +71,157 @@
gateway_ip: false,
subnet_mask: false,
dns_1: false,
dns_2: false,
});
dns_2: false
})
let formErrorhostname = $state(false);
let formErrorhostname = $state(false)
async function getWifiStatus() {
const result = await api.get<WifiStatus>('/api/wifi/sta/status');
const result = await api.get<WifiStatus>('/api/wifi/sta/status')
if (result.isErr()) {
console.error(`Error occurred while fetching: `, result.inner);
return;
console.error(`Error occurred while fetching: `, result.inner)
return
}
wifiStatus = result.inner;
return wifiStatus;
wifiStatus = result.inner
return wifiStatus
}
async function getWifiSettings() {
const result = await api.get<WifiSettings>('/api/wifi/sta/settings');
const result = await api.get<WifiSettings>('/api/wifi/sta/settings')
if (result.isErr()) {
console.error(`Error occurred while fetching: `, result.inner);
return;
console.error(`Error occurred while fetching: `, result.inner)
return
}
wifiSettings = result.inner;
dndNetworkList = wifiSettings.wifi_networks;
return wifiSettings;
wifiSettings = result.inner
dndNetworkList = wifiSettings.wifi_networks
return wifiSettings
}
onDestroy(() => socket.off('WiFiSettings'));
onDestroy(() => socket.off(Topics.WiFiSettings))
onMount(() => {
socket.on<WifiSettings>('WiFiSettings', data => {
wifiSettings = data;
dndNetworkList = wifiSettings.wifi_networks;
});
});
socket.on<WifiSettings>(Topics.WiFiSettings, data => {
wifiSettings = data
dndNetworkList = wifiSettings.wifi_networks
})
})
async function postWiFiSettings(data: WifiSettings) {
const result = await api.post<WifiSettings>('/api/wifi/sta/settings', data);
const result = await api.post<WifiSettings>('/api/wifi/sta/settings', data)
if (result.isErr()) {
console.error(`Error occurred while fetching: `, result.inner);
notifications.error('User not authorized.', 3000);
return;
console.error(`Error occurred while fetching: `, result.inner)
notifications.error('User not authorized.', 3000)
return
}
wifiSettings = result.inner;
notifications.success('Wi-Fi settings updated.', 3000);
wifiSettings = result.inner
notifications.success('Wi-Fi settings updated.', 3000)
}
function validateHostName() {
if (!wifiSettings) return false;
if (!wifiSettings) return false
if (wifiSettings.hostname.length < 3 || wifiSettings.hostname.length > 32) {
formErrorhostname = true;
formErrorhostname = true
} else {
formErrorhostname = false;
formErrorhostname = false
// Update global wifiSettings object
wifiSettings.wifi_networks = dndNetworkList;
wifiSettings.wifi_networks = dndNetworkList
// Post to REST API
postWiFiSettings(wifiSettings);
console.log(wifiSettings);
postWiFiSettings(wifiSettings)
console.log(wifiSettings)
}
}
function validateWiFiForm(event: SubmitEvent) {
event.preventDefault();
let valid = true;
event.preventDefault()
let valid = true
// Validate SSID
if (networkEditable.ssid.length < 3 || networkEditable.ssid.length > 32) {
valid = false;
formErrors.ssid = true;
valid = false
formErrors.ssid = true
} else {
formErrors.ssid = false;
formErrors.ssid = false
}
networkEditable.static_ip_config = static_ip_config;
networkEditable.static_ip_config = static_ip_config
if (networkEditable.static_ip_config) {
// RegEx for IPv4
const regexExp =
/\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/;
/\b(?:(?:2(?:[0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9])\.){3}(?:(?:2([0-4][0-9]|5[0-5])|[0-1]?[0-9]?[0-9]))\b/
// Validate gateway IP
if (!regexExp.test(networkEditable.gateway_ip!)) {
valid = false;
formErrors.gateway_ip = true;
valid = false
formErrors.gateway_ip = true
} else {
formErrors.gateway_ip = false;
formErrors.gateway_ip = false
}
// Validate Subnet Mask
if (!regexExp.test(networkEditable.subnet_mask!)) {
valid = false;
formErrors.subnet_mask = true;
valid = false
formErrors.subnet_mask = true
} else {
formErrors.subnet_mask = false;
formErrors.subnet_mask = false
}
// Validate local IP
if (!regexExp.test(networkEditable.local_ip!)) {
valid = false;
formErrors.local_ip = true;
valid = false
formErrors.local_ip = true
} else {
formErrors.local_ip = false;
formErrors.local_ip = false
}
// Validate DNS 1
if (!regexExp.test(networkEditable.dns_ip_1!)) {
valid = false;
formErrors.dns_1 = true;
valid = false
formErrors.dns_1 = true
} else {
formErrors.dns_1 = false;
formErrors.dns_1 = false
}
// Validate DNS 2
if (!regexExp.test(networkEditable.dns_ip_2!)) {
valid = false;
formErrors.dns_2 = true;
valid = false
formErrors.dns_2 = true
} else {
formErrors.dns_2 = false;
formErrors.dns_2 = false
}
} else {
formErrors.local_ip = false;
formErrors.subnet_mask = false;
formErrors.gateway_ip = false;
formErrors.dns_1 = false;
formErrors.dns_2 = false;
formErrors.local_ip = false
formErrors.subnet_mask = false
formErrors.gateway_ip = false
formErrors.dns_1 = false
formErrors.dns_2 = false
}
// Submit JSON to REST API
if (valid) {
if (newNetwork) {
dndNetworkList.push(networkEditable);
dndNetworkList.push(networkEditable)
} else {
dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable);
dndNetworkList.splice(dndNetworkList.indexOf(networkEditable), 1, networkEditable)
}
addNetwork();
dndNetworkList = [...dndNetworkList]; //Trigger reactivity
showNetworkEditor = false;
addNetwork()
dndNetworkList = [...dndNetworkList] //Trigger reactivity
showNetworkEditor = false
}
}
function scanForNetworks() {
modals.open(ScanNetworks, {
storeNetwork: (network: string) => {
addNetwork();
networkEditable.ssid = network;
showNetworkEditor = true;
modals.close();
},
});
addNetwork()
networkEditable.ssid = network
showNetworkEditor = true
modals.close()
}
})
}
function addNetwork() {
newNetwork = true;
newNetwork = true
networkEditable = {
ssid: '',
password: '',
@@ -225,14 +230,14 @@
subnet_mask: undefined,
gateway_ip: undefined,
dns_ip_1: undefined,
dns_ip_2: undefined,
};
dns_ip_2: undefined
}
}
function handleEdit(index: number) {
newNetwork = false;
showNetworkEditor = true;
networkEditable = dndNetworkList[index];
newNetwork = false
showNetworkEditor = true
networkEditable = dndNetworkList[index]
}
function confirmDelete(index: number) {
@@ -241,20 +246,20 @@
message: 'Are you sure you want to delete this network?',
labels: {
cancel: { label: 'Cancel', icon: Cancel },
confirm: { label: 'Delete', icon: Delete },
confirm: { label: 'Delete', icon: Delete }
},
onConfirm: () => {
// Check if network is currently been edited and delete as well
if (dndNetworkList[index].ssid === networkEditable.ssid) {
addNetwork();
addNetwork()
}
// Remove network from array
dndNetworkList.splice(index, 1);
dndNetworkList = [...dndNetworkList]; //Trigger reactivity
showNetworkEditor = false;
modals.close();
},
});
dndNetworkList.splice(index, 1)
dndNetworkList = [...dndNetworkList] //Trigger reactivity
showNetworkEditor = false
modals.close()
}
})
}
function checkNetworkList() {
@@ -264,21 +269,21 @@
message:
'You have reached the maximum number of networks. Please delete one to add another.',
dismiss: { label: 'OK', icon: Check },
onDismiss: () => modals.close(),
});
return false;
onDismiss: () => modals.close()
})
return false
} else {
return true;
return true
}
}
function onDrop({ detail: { from, to } }: CustomEvent<DropEvent>) {
if (!to || from === to) {
return;
return
}
dndNetworkList = reorder(dndNetworkList, from.index, to.index);
console.log(dndNetworkList);
dndNetworkList = reorder(dndNetworkList, from.index, to.index)
console.log(dndNetworkList)
}
</script>
@@ -312,7 +317,7 @@
<button
class="btn btn-circle btn-ghost btn-sm modal-button"
onclick={() => {
showWifiDetails = !showWifiDetails;
showWifiDetails = !showWifiDetails
}}>
<Down
class="text-base-content h-auto w-6 transition-transform duration-300 ease-in-out {(
@@ -359,8 +364,8 @@
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-16"
onclick={() => {
if (checkNetworkList()) {
addNetwork();
showNetworkEditor = true;
addNetwork()
showNetworkEditor = true
}
}}>
<Add class="h-6 w-6" /></button>
@@ -368,8 +373,8 @@
class="btn btn-primary text-primary-content btn-md absolute -top-14 right-0"
onclick={() => {
if (checkNetworkList()) {
scanForNetworks();
showNetworkEditor = true;
scanForNetworks()
showNetworkEditor = true
}
}}>
<Scan class="h-6 w-6" /></button>
@@ -389,13 +394,13 @@
<button
class="btn btn-ghost btn-sm"
onclick={() => {
handleEdit(index);
handleEdit(index)
}}>
<Edit class="h-6 w-6" /></button>
<button
class="btn btn-ghost btn-sm"
onclick={() => {
confirmDelete(index);
confirmDelete(index)
}}>
<Delete class="text-error h-6 w-6" />
</button>