diff --git a/src/hooks.client.ts b/src/hooks.client.ts index 7cba2f5e..db20e461 100644 --- a/src/hooks.client.ts +++ b/src/hooks.client.ts @@ -1,5 +1,5 @@ import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; -import { initializeSignalR } from '$lib/signalr'; +import { initializeSignalR } from '$lib/signalr/index.svelte'; import { backendMetadata } from '$lib/state/BackendMetadata.svelte'; import { initializeDarkModeStore } from '$lib/stores/ColorSchemeStore.svelte'; import { initializeSerialPortsStore } from '$lib/stores/SerialPortsStore'; diff --git a/src/lib/components/ControlModules/ClassicControlModule.svelte b/src/lib/components/ControlModules/ClassicControlModule.svelte index f78a414d..e9c6ffb7 100644 --- a/src/lib/components/ControlModules/ClassicControlModule.svelte +++ b/src/lib/components/ControlModules/ClassicControlModule.svelte @@ -6,7 +6,7 @@ ControlIntensityDefault, ControlIntensityProps, } from '$lib/constants/ControlConstants'; - import { SignalR_Connection } from '$lib/signalr'; + import { getConnection } from '$lib/signalr/index.svelte'; import { ControlType } from '$lib/signalr/models/ControlType'; import { serializeControlMessages } from '$lib/signalr/serializers/Control'; import ControlListener from './ControlListener.svelte'; @@ -26,8 +26,9 @@ let active = $state(null); function ctrl(type: ControlType) { - if (!$SignalR_Connection) return; - serializeControlMessages($SignalR_Connection, [{ id: shocker.id, type, intensity, duration }]); + const conn = getConnection(); + if (!conn) return; + serializeControlMessages(conn, [{ id: shocker.id, type, intensity, duration }]); } diff --git a/src/lib/components/ControlModules/MapControlModule.svelte b/src/lib/components/ControlModules/MapControlModule.svelte index f91ebcd9..39f7034b 100644 --- a/src/lib/components/ControlModules/MapControlModule.svelte +++ b/src/lib/components/ControlModules/MapControlModule.svelte @@ -1,7 +1,7 @@ diff --git a/src/lib/components/ControlModules/SharedShockerControlModule.svelte b/src/lib/components/ControlModules/SharedShockerControlModule.svelte index 3916bf38..6cca0672 100644 --- a/src/lib/components/ControlModules/SharedShockerControlModule.svelte +++ b/src/lib/components/ControlModules/SharedShockerControlModule.svelte @@ -7,7 +7,7 @@ ControlIntensityDefault, ControlIntensityProps, } from '$lib/constants/ControlConstants'; - import { SignalR_Connection } from '$lib/signalr'; + import { getConnection } from '$lib/signalr/index.svelte'; import { ControlType } from '$lib/signalr/models/ControlType'; import { serializeControlMessages } from '$lib/signalr/serializers/Control'; import ControlListener from './ControlListener.svelte'; @@ -42,8 +42,9 @@ const clampedDuration = $derived(Math.min(duration, maxDurationSeconds)); function ctrl(type: ControlType) { - if (!$SignalR_Connection) return; - serializeControlMessages($SignalR_Connection, [ + const conn = getConnection(); + if (!conn) return; + serializeControlMessages(conn, [ { id: shocker.id, type, diff --git a/src/lib/components/ControlModules/SimpleControlModule.svelte b/src/lib/components/ControlModules/SimpleControlModule.svelte index c4902f74..39e7d98a 100644 --- a/src/lib/components/ControlModules/SimpleControlModule.svelte +++ b/src/lib/components/ControlModules/SimpleControlModule.svelte @@ -1,6 +1,6 @@ diff --git a/src/lib/components/confirm-dialog/dialog-confirm.svelte b/src/lib/components/confirm-dialog/dialog-confirm.svelte deleted file mode 100644 index 595a5516..00000000 --- a/src/lib/components/confirm-dialog/dialog-confirm.svelte +++ /dev/null @@ -1,55 +0,0 @@ - - - open, (o) => (open = o)}> - - - {title} - - {desc} - {#if descSnippet} - {@render descSnippet(data)} - {/if} - - - - - diff --git a/src/lib/components/confirm-dialog/dialog-manager.svelte b/src/lib/components/confirm-dialog/dialog-manager.svelte deleted file mode 100644 index 5423abce..00000000 --- a/src/lib/components/confirm-dialog/dialog-manager.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - -{#key dialogCounter} - {#if dialogData} - - {/if} -{/key} diff --git a/src/lib/components/dialog-manager/dialog-alert-content.svelte b/src/lib/components/dialog-manager/dialog-alert-content.svelte new file mode 100644 index 00000000..e8b4ea1f --- /dev/null +++ b/src/lib/components/dialog-manager/dialog-alert-content.svelte @@ -0,0 +1,21 @@ + + + + {title} + {#if desc} + {desc} + {/if} + + + + diff --git a/src/lib/components/dialog-manager/dialog-confirm-content.svelte b/src/lib/components/dialog-manager/dialog-confirm-content.svelte new file mode 100644 index 00000000..48b7ca44 --- /dev/null +++ b/src/lib/components/dialog-manager/dialog-confirm-content.svelte @@ -0,0 +1,45 @@ + + + + {title} + + {desc} + {#if descSnippet} + {@render descSnippet(data as T)} + {/if} + + + + + + diff --git a/src/lib/components/dialog-manager/dialog-custom-content.svelte b/src/lib/components/dialog-manager/dialog-custom-content.svelte new file mode 100644 index 00000000..38f91730 --- /dev/null +++ b/src/lib/components/dialog-manager/dialog-custom-content.svelte @@ -0,0 +1,16 @@ + + +{@render contentSnippet(renderProps)} diff --git a/src/lib/components/dialog-manager/dialog-manager.svelte b/src/lib/components/dialog-manager/dialog-manager.svelte new file mode 100644 index 00000000..5d4749c0 --- /dev/null +++ b/src/lib/components/dialog-manager/dialog-manager.svelte @@ -0,0 +1,35 @@ + + +{#if dialogId && ctx} + {#key dialogId} + open, handleOpenChange}> + + + + + {/key} +{/if} diff --git a/src/lib/components/dialog-manager/dialog-store.svelte.ts b/src/lib/components/dialog-manager/dialog-store.svelte.ts new file mode 100644 index 00000000..b9e32436 --- /dev/null +++ b/src/lib/components/dialog-manager/dialog-store.svelte.ts @@ -0,0 +1,96 @@ +import { SvelteMap } from 'svelte/reactivity'; +import DialogAlertContent from './dialog-alert-content.svelte'; +import DialogConfirmContent from './dialog-confirm-content.svelte'; +import DialogCustomContent from './dialog-custom-content.svelte'; +import type { + AlertDialogOptions, + ConfirmDialogOptions, + ConfirmResult, + CustomDialogOptions, + DialogContext, +} from './types'; + +// State +let dialogCount = $state(0); +const dialogs = new SvelteMap(); + +export function getOldestDialog(): [number, DialogContext] | null { + const firstEntry = dialogs.entries().next(); + return firstEntry.done ? null : firstEntry.value; +} + +export function removeDialog(id: number): void { + dialogs.delete(id); +} + +// Helper to create dialog with common setup +export function createDialog( + contextFactory: (resolve: (result: R) => void) => DialogContext +): Promise { + return new Promise((resolve) => { + const id = ++dialogCount; + let resolved = false; + + const wrappedResolve = (result: R) => { + if (resolved) return; + resolved = true; + setTimeout(() => removeDialog(id), 150); + resolve(result); + }; + + dialogs.set(id, contextFactory(wrappedResolve) as DialogContext); + }); +} + +/** + * Opens a fully custom dialog with your own content snippet. + */ +export function open(options: CustomDialogOptions): Promise { + return createDialog((resolve) => ({ + content: DialogCustomContent, + props: { + data: options.data, + contentSnippet: options.contentSnippet, + resolve, + close: () => resolve(undefined as R), + }, + resolve, + })); +} + +/** + * Opens a confirm dialog with built-in confirm/cancel buttons. + */ +export function confirm(options: ConfirmDialogOptions): Promise> { + return createDialog>((resolve) => ({ + content: DialogConfirmContent, + props: { + ...options, + resolve, + close: () => resolve({ confirmed: false }), + }, + resolve, + })); +} + +/** + * Opens an alert dialog that the user acknowledges. + */ +export function alert(options: AlertDialogOptions): Promise { + return createDialog((resolve) => ({ + content: DialogAlertContent, + props: { + ...options, + resolve, + close: () => resolve(), + }, + resolve, + })); +} + +export const dialog = { + open, + confirm, + alert, + createDialog, +}; diff --git a/src/lib/components/dialog-manager/types.ts b/src/lib/components/dialog-manager/types.ts new file mode 100644 index 00000000..46b121ef --- /dev/null +++ b/src/lib/components/dialog-manager/types.ts @@ -0,0 +1,59 @@ +import type { Component, Snippet } from 'svelte'; + +// Props passed to dialog content components +export interface DialogContentProps { + resolve: (result: R) => void; + close: () => void; +} + +// Props for custom dialog snippets +export interface DialogRenderProps extends DialogContentProps { + data: T; +} + +// Generic dialog context - stores a component to render +export interface DialogContext { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + content: Component; + props: Record; + resolve: (result: R) => void; +} + +// Result types +export type ConfirmResult = { confirmed: true; data: T } | { confirmed: false }; + +// Options for each dialog type +export interface CustomDialogOptions { + data?: T; + contentSnippet: Snippet<[DialogRenderProps]>; +} + +export interface ConfirmDialogOptions { + title: string; + desc?: string; + data?: T; + confirmButtonText?: string; + cancelButtonText?: string; + descSnippet?: Snippet<[T]>; +} + +export interface AlertDialogOptions { + title: string; + desc?: string; + buttonText?: string; +} + +export interface AlertProps extends AlertDialogOptions { + resolve: () => void; + close: () => void; +} + +export interface ConfirmProps extends ConfirmDialogOptions { + resolve: (result: ConfirmResult) => void; + close: () => void; +} + +export interface CustomProps extends CustomDialogOptions { + resolve: (result: R) => void; + close: () => void; +} diff --git a/src/lib/signalr/handlers/DeviceStatus.ts b/src/lib/signalr/handlers/DeviceStatus.ts index 7e1cea13..88eafd1e 100644 --- a/src/lib/signalr/handlers/DeviceStatus.ts +++ b/src/lib/signalr/handlers/DeviceStatus.ts @@ -1,5 +1,5 @@ import { isDeviceOnlineState } from '$lib/signalr/models/DeviceOnlineState'; -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { HubOnlineState, onlineHubs } from '$lib/stores/HubsStore.svelte'; import { toast } from 'svelte-sonner'; export function handleSignalrDeviceStatus(array: unknown) { @@ -9,18 +9,16 @@ export function handleSignalrDeviceStatus(array: unknown) { return; } - OnlineHubsStore.update((state) => { - array.forEach((entry) => { - const existing = state.get(entry.device); - state.set(entry.device, { - hubId: entry.device, - isOnline: entry.online, - firmwareVersion: entry.firmwareVersion, - otaInstall: existing?.otaInstall ?? null, - otaResult: existing?.otaResult ?? null, - }); - }); - - return state; - }); + for (const entry of array) { + const existing = onlineHubs.get(entry.device); + if (existing) { + existing.isOnline = entry.online; + existing.firmwareVersion = entry.firmwareVersion; + } else { + onlineHubs.set( + entry.device, + new HubOnlineState(entry.device, entry.online, entry.firmwareVersion) + ); + } + } } diff --git a/src/lib/signalr/handlers/DeviceUpdate.ts b/src/lib/signalr/handlers/DeviceUpdate.ts index 535bcfd9..806ba8f2 100644 --- a/src/lib/signalr/handlers/DeviceUpdate.ts +++ b/src/lib/signalr/handlers/DeviceUpdate.ts @@ -1,5 +1,5 @@ import { HubUpdateType, isHubUpdateType } from '$lib/signalr/models/HubUpdateType'; -import { refreshOwnHubs } from '$lib/stores/HubsStore'; +import { refreshOwnHubs } from '$lib/stores/HubsStore.svelte'; import { isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; diff --git a/src/lib/signalr/handlers/OtaInstallFailed.ts b/src/lib/signalr/handlers/OtaInstallFailed.ts index 2603bc34..f5461973 100644 --- a/src/lib/signalr/handlers/OtaInstallFailed.ts +++ b/src/lib/signalr/handlers/OtaInstallFailed.ts @@ -1,4 +1,4 @@ -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { isBoolean, isNumber, isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; @@ -17,14 +17,11 @@ export function handleSignalrOtaInstallFailed( return; } - OnlineHubsStore.update((hubs) => { - const hub = hubs.get(hubId); - if (hub && hub.otaInstall?.id === updateId) { - hub.otaInstall = null; - hub.otaResult = { success: false, message }; - } - return hubs; - }); + const hub = onlineHubs.get(hubId); + if (hub && hub.otaInstall?.id === updateId) { + hub.otaInstall = null; + hub.otaResult = { success: false, message }; + } toast.error(`Hub firmware update failed: ${message}`); } diff --git a/src/lib/signalr/handlers/OtaInstallProgress.ts b/src/lib/signalr/handlers/OtaInstallProgress.ts index 97dab815..4a8805a7 100644 --- a/src/lib/signalr/handlers/OtaInstallProgress.ts +++ b/src/lib/signalr/handlers/OtaInstallProgress.ts @@ -1,5 +1,5 @@ import { isOtaUpdateProgressTask } from '$lib/signalr/models/OtaUpdateProgressTask'; -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { isNumber, isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; @@ -23,12 +23,8 @@ export function handleSignalrOtaInstallProgress( return; } - OnlineHubsStore.update((hubs) => { - const hub = hubs.get(hubId); - if (hub && hub.otaInstall?.id === updateId) { - hub.otaInstall.task = task; - hub.otaInstall.progress = progress; - } - return hubs; - }); + const hub = onlineHubs.get(hubId); + if (hub && hub.otaInstall?.id === updateId) { + hub.otaInstall = { ...hub.otaInstall, task, progress }; + } } diff --git a/src/lib/signalr/handlers/OtaInstallStarted.ts b/src/lib/signalr/handlers/OtaInstallStarted.ts index e073c695..bb3d2c5f 100644 --- a/src/lib/signalr/handlers/OtaInstallStarted.ts +++ b/src/lib/signalr/handlers/OtaInstallStarted.ts @@ -1,4 +1,4 @@ -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { isNumber, isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; @@ -16,16 +16,8 @@ export function handleSignalrOtaInstallStarted( return; } - OnlineHubsStore.update((hubs) => { - const hub = hubs.get(hubId); - if (hub) { - hub.otaInstall = { - id: updateId, - version: targetVersion, - task: 0, - progress: 0, - }; - } - return hubs; - }); + const hub = onlineHubs.get(hubId); + if (hub) { + hub.otaInstall = { id: updateId, version: targetVersion, task: 0, progress: 0 }; + } } diff --git a/src/lib/signalr/handlers/OtaInstallSucceeded.ts b/src/lib/signalr/handlers/OtaInstallSucceeded.ts index dcf869df..5c38740a 100644 --- a/src/lib/signalr/handlers/OtaInstallSucceeded.ts +++ b/src/lib/signalr/handlers/OtaInstallSucceeded.ts @@ -1,4 +1,4 @@ -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { isNumber, isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; @@ -12,14 +12,11 @@ export function handleSignalrOtaInstallSucceeded(hubId: unknown, updateId: unkno return; } - OnlineHubsStore.update((hubs) => { - const hub = hubs.get(hubId); - if (hub && hub.otaInstall?.id === updateId) { - hub.otaInstall = null; - hub.otaResult = { success: true, message: 'Update completed successfully' }; - } - return hubs; - }); + const hub = onlineHubs.get(hubId); + if (hub && hub.otaInstall?.id === updateId) { + hub.otaInstall = null; + hub.otaResult = { success: true, message: 'Update completed successfully' }; + } toast.success('Hub firmware update completed successfully!'); } diff --git a/src/lib/signalr/handlers/OtaRollback.ts b/src/lib/signalr/handlers/OtaRollback.ts index 6380e790..ca27d798 100644 --- a/src/lib/signalr/handlers/OtaRollback.ts +++ b/src/lib/signalr/handlers/OtaRollback.ts @@ -1,4 +1,4 @@ -import { OnlineHubsStore } from '$lib/stores/HubsStore'; +import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { isNumber, isString } from '$lib/typeguards'; import { toast } from 'svelte-sonner'; @@ -12,14 +12,11 @@ export function handleSignalrOtaRollback(hubId: unknown, updateId: unknown): voi return; } - OnlineHubsStore.update((hubs) => { - const hub = hubs.get(hubId); - if (hub && hub.otaInstall?.id === updateId) { - hub.otaInstall = null; - hub.otaResult = { success: false, message: 'Device rolled back to previous version' }; - } - return hubs; - }); + const hub = onlineHubs.get(hubId); + if (hub && hub.otaInstall?.id === updateId) { + hub.otaInstall = null; + hub.otaResult = { success: false, message: 'Device rolled back to previous version' }; + } toast.warning('Hub firmware rolled back to previous version'); } diff --git a/src/lib/signalr/index.ts b/src/lib/signalr/index.svelte.ts similarity index 66% rename from src/lib/signalr/index.ts rename to src/lib/signalr/index.svelte.ts index 23e71210..c05ad70c 100644 --- a/src/lib/signalr/index.ts +++ b/src/lib/signalr/index.svelte.ts @@ -8,7 +8,6 @@ import { LogLevel, } from '@microsoft/signalr'; import { toast } from 'svelte-sonner'; -import { type Readable, get, writable } from 'svelte/store'; import { handleSignalrDeviceStatus, handleSignalrDeviceUpdate, @@ -22,11 +21,18 @@ import { const BackendHubUserUrl = getBackendURL('1/hubs/user').href; -const signalr_connection = writable(null); -const signalr_state = writable(HubConnectionState.Disconnected); +let connection = $state(null); +let connectionState = $state(HubConnectionState.Disconnected); + +export function getConnection(): HubConnection | null { + return connection; +} + +export function getConnectionState(): HubConnectionState { + return connectionState; +} export async function initializeSignalR() { - let connection = get(signalr_connection); if (connection) { return; } @@ -41,21 +47,21 @@ export async function initializeSignalR() { .build(); connection.onclose(() => { - signalr_state.set(HubConnectionState.Disconnected); + connectionState = HubConnectionState.Disconnected; }); connection.onreconnecting(() => { - signalr_state.set(HubConnectionState.Reconnecting); + connectionState = HubConnectionState.Reconnecting; }); connection.onreconnected(() => { - signalr_state.set(HubConnectionState.Connected); + connectionState = HubConnectionState.Connected; }); // Look up in OpenShock API repository: Common/Hubs/IUserHub.cs connection.on('Welcome', () => { // Arg is the SignalR connectionId - signalr_state.set(HubConnectionState.Connected); + connectionState = HubConnectionState.Connected; }); connection.on('Log', handleSignalrLog); @@ -69,39 +75,26 @@ export async function initializeSignalR() { connection.on('OtaInstallFailed', handleSignalrOtaInstallFailed); connection.on('OtaRollback', handleSignalrOtaRollback); - signalr_connection.set(connection); - try { await connection.start(); - signalr_state.set(HubConnectionState.Connected); + connectionState = HubConnectionState.Connected; } catch (error) { console.error(error); toast.error('Failed to connect to server!'); - signalr_state.set(HubConnectionState.Disconnected); + connectionState = HubConnectionState.Disconnected; } } export async function destroySignalR() { - if (!signalr_connection) return; + if (!connection) return; try { - const connection = get(signalr_connection); - if (connection) { - await connection.stop(); - } + await connection.stop(); } catch (error) { console.error(error); toast.error('Encountered error while disconnecting from server!'); } finally { - signalr_connection.set(null); - signalr_state.set(HubConnectionState.Disconnected); + connection = null; + connectionState = HubConnectionState.Disconnected; } } - -export const SignalR_State = { - subscribe: signalr_state.subscribe, -} as Readable; - -export const SignalR_Connection = { - subscribe: signalr_connection.subscribe, -} as Readable; diff --git a/src/lib/stores/ConfirmDialogStore.ts b/src/lib/stores/ConfirmDialogStore.ts deleted file mode 100644 index 0b8addb8..00000000 --- a/src/lib/stores/ConfirmDialogStore.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Snippet } from 'svelte'; -import { writable } from 'svelte/store'; - -export interface ConfirmDialogContext { - data: T; - onConfirm: (value: T) => void; - title: string; - desc?: string; - confirmButtonText?: string; - descSnippet?: Snippet<[T]>; -} - -/* eslint-disable-next-line @typescript-eslint/no-explicit-any -- store holds generic dialog context, concrete type varies per caller */ -export const ConfirmDialogStore = writable | null>(null); - -export function openConfirmDialog(context: ConfirmDialogContext) { - ConfirmDialogStore.set(context); -} diff --git a/src/lib/stores/HubsStore.svelte.ts b/src/lib/stores/HubsStore.svelte.ts new file mode 100644 index 00000000..cd0ab19e --- /dev/null +++ b/src/lib/stores/HubsStore.svelte.ts @@ -0,0 +1,52 @@ +import { shockersV1Api } from '$lib/api'; +import type { DeviceWithShockersResponse } from '$lib/api/internal/v1'; +import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; +import type { OtaUpdateProgressTask } from '$lib/signalr/models/OtaUpdateProgressTask'; +import { SvelteMap } from 'svelte/reactivity'; + +export type OwnHub = DeviceWithShockersResponse; + +export type OtaInstallState = { + id: number; + version: string; + task: OtaUpdateProgressTask; + progress: number; +}; + +export type OtaResultState = { + success: boolean; + message: string; +}; + +export class HubOnlineState { + hubId: string; + isOnline = $state(false); + firmwareVersion = $state(null); + otaInstall = $state(null); + otaResult = $state(null); + + constructor(hubId: string, isOnline: boolean, firmwareVersion: string | null) { + this.hubId = hubId; + this.isOnline = isOnline; + this.firmwareVersion = firmwareVersion; + } +} + +export const ownHubs = new SvelteMap(); +export const onlineHubs = new SvelteMap(); + +export async function refreshOwnHubs() { + try { + const response = await shockersV1Api.shockerListShockers(); + if (!response.data) { + throw new Error(`Failed to fetch devices: ${response.message}`); + } + + ownHubs.clear(); + for (const d of response.data) { + ownHubs.set(d.id, d); + } + } catch (error) { + handleApiError(error); + } +} diff --git a/src/lib/stores/HubsStore.ts b/src/lib/stores/HubsStore.ts deleted file mode 100644 index bc1caba5..00000000 --- a/src/lib/stores/HubsStore.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { shockersV1Api } from '$lib/api'; -import type { DeviceWithShockersResponse } from '$lib/api/internal/v1'; -import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; -import type { OtaUpdateProgressTask } from '$lib/signalr/models/OtaUpdateProgressTask'; -import { writable } from 'svelte/store'; - -export type OwnHub = DeviceWithShockersResponse; -export type HubOnlineState = { - hubId: string; - isOnline: boolean; - firmwareVersion: string | null; - otaInstall: { - id: number; - version: string; - task: OtaUpdateProgressTask; - progress: number; - } | null; - otaResult: { success: boolean; message: string } | null; -}; - -export const OwnHubsStore = writable>(new Map()); -export const OnlineHubsStore = writable>(new Map()); - -export function refreshOwnHubs() { - shockersV1Api - .shockerListShockers() - .then((response) => { - if (!response.data) { - throw new Error(`Failed to fetch devices: ${response.message}`); - } - - OwnHubsStore.set(new Map(response.data.map((d) => [d.id, d]))); - }) - .catch(handleApiError); -} diff --git a/src/routes/(anonymous)/login/+page.svelte b/src/routes/(anonymous)/login/+page.svelte index 615e8eea..6c1b137c 100644 --- a/src/routes/(anonymous)/login/+page.svelte +++ b/src/routes/(anonymous)/login/+page.svelte @@ -15,7 +15,7 @@ import Turnstile from '$lib/components/Turnstile.svelte'; import { accountV2Api } from '$lib/api'; import { UserStore } from '$lib/stores/UserStore'; - import { initializeSignalR } from '$lib/signalr'; + import { initializeSignalR } from '$lib/signalr/index.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; import { isValidationError, mapToValRes } from '$lib/errorhandling/ValidationProblemDetails'; import OauthButtons from '$lib/components/auth/oauth-buttons.svelte'; diff --git a/src/routes/(anonymous)/logout/+page.ts b/src/routes/(anonymous)/logout/+page.ts index 4bf89455..8b36d09f 100644 --- a/src/routes/(anonymous)/logout/+page.ts +++ b/src/routes/(anonymous)/logout/+page.ts @@ -2,7 +2,7 @@ import { browser } from '$app/environment'; import { goto } from '$app/navigation'; import { resolve } from '$app/paths'; import { accountV1Api } from '$lib/api'; -import { destroySignalR } from '$lib/signalr'; +import { destroySignalR } from '$lib/signalr/index.svelte'; import { UserStore } from '$lib/stores/UserStore'; export const prerender = false; diff --git a/src/routes/(authenticated)/hubs/+page.svelte b/src/routes/(authenticated)/hubs/+page.svelte index 52e4f12f..30590885 100644 --- a/src/routes/(authenticated)/hubs/+page.svelte +++ b/src/routes/(authenticated)/hubs/+page.svelte @@ -1,34 +1,33 @@ - +{#snippet createHubSnippet( + props: DialogRenderProps<{ name: string }, { name: string } | undefined> +)} + + Create Hub + + + +{/snippet} Hubs
- @@ -70,26 +93,61 @@ This is a list of all hubs you own. -
+
{#if isMobile.current} - {#each data as hub (hub.id)} -
-
- -
- {hub.name} - {#if hub.firmware_version} - {hub.firmware_version} - {:else} - Offline - {/if} +
+ {#each data as hub (hub.id)} +
+
+ +
+ {hub.name} + {#if hub.is_online && hub.firmware_version} + {hub.firmware_version} + {:else} + Offline + {/if} +
+
- -
- {/each} + {/each} +
{:else} - + + + + Name + Status + Version + Created + + + + + {#each data as hub (hub.id)} + + {hub.name} + + {#if hub.is_online} + Online + {:else} + Offline + {/if} + + {hub.firmware_version ?? '—'} + {hub.created_at.toLocaleDateString()} + + + + + {:else} + + No hubs found. + + {/each} + + {/if}
diff --git a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte index 9b4df390..b3c3855d 100644 --- a/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte +++ b/src/routes/(authenticated)/hubs/[hubId=guid]/update/+page.svelte @@ -12,16 +12,16 @@ import { Progress } from '$lib/components/ui/progress'; import * as Table from '$lib/components/ui/table'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { SignalR_Connection } from '$lib/signalr'; + import { getConnection } from '$lib/signalr/index.svelte'; import { OtaUpdateProgressTask } from '$lib/signalr/models/OtaUpdateProgressTask'; import { serializeOtaInstallMessage } from '$lib/signalr/serializers/OtaInstall'; import { breadcrumbs } from '$lib/state/Breadcrumbs.svelte'; import { - type HubOnlineState, - OnlineHubsStore, - OwnHubsStore, + HubOnlineState, + onlineHubs, + ownHubs, refreshOwnHubs, - } from '$lib/stores/HubsStore'; + } from '$lib/stores/HubsStore.svelte'; import { cn } from '$lib/utils'; import { NumberToHexPadded } from '$lib/utils/convert'; import { onMount } from 'svelte'; @@ -85,21 +85,10 @@ let hub = $derived.by(() => { if (!hubLoaded) return null; const hubId = page.params.hubId ?? ''; - const h = $OnlineHubsStore.get(hubId); - if (!h) { - return { - hubId, - isOnline: false, - firmwareVersion: null, - otaInstall: null, - otaResult: null, - }; - } - // Spread to create a new reference so downstream deriveds re-evaluate on store updates - return { ...h }; + return onlineHubs.get(hubId) ?? new HubOnlineState(hubId, false, null); }); - let hubName = $derived($OwnHubsStore.get(page.params.hubId ?? '')?.name ?? 'Unknown Hub'); + let hubName = $derived(ownHubs.get(page.params.hubId ?? '')?.name ?? 'Unknown Hub'); let isUpdating = $derived(hub?.otaInstall !== null && hub?.otaInstall !== undefined); @@ -160,25 +149,20 @@ function startUpdate() { const hubId = page.params.hubId; - if ($SignalR_Connection === null || !hubId || hub === null || version === null) return; + const conn = getConnection(); + if (conn === null || !hubId || hub === null || version === null) return; // Clear any previous result - OnlineHubsStore.update((hubs) => { - const h = hubs.get(hubId); - if (h) h.otaResult = null; - return hubs; - }); - serializeOtaInstallMessage($SignalR_Connection, hubId, version); + const h = onlineHubs.get(hubId); + if (h) h.otaResult = null; + serializeOtaInstallMessage(conn, hubId, version); confirmOpen = false; } function resetResult() { const hubId = page.params.hubId; if (!hubId) return; - OnlineHubsStore.update((hubs) => { - const h = hubs.get(hubId); - if (h) h.otaResult = null; - return hubs; - }); + const h = onlineHubs.get(hubId); + if (h) h.otaResult = null; } let isLoading = $state(false); @@ -305,10 +289,7 @@ +{/snippet} + +{#snippet deleteDescSnippet(h: Hub)} + You are about to delete hub "{h.name}"
+ This will also delete the following shockers: +
+ {#each h.shockers as shocker (shocker.id)} + {shocker.name} + {/each} +
+{/snippet} + +{#snippet pairSnippet(props: DialogRenderProps<{ loading: boolean; code: string | null }>)} + + + {props.data.loading + ? 'Generating...' + : props.data.code + ? 'Pair code generated' + : 'Generate pair code?'} + + {#if !props.data.loading} + + {#if props.data.code} + Pair code generated for {hub.name}
+ The code below will not be accessible later, please copy it now and update clients with it + {:else} + You are about to generate a pair code for {hub.name}
+ It will be valid for 15 minutes after its creation.
+ There is only one active pair code per hub, newly generated ones will override the older active + ones. + {/if} +
+ {/if} +
+ {#if !props.data.loading} + {#if props.data.code} +
+ Pair code: + +
+ + {:else} + + {/if} + {/if} +{/snippet} + +{#snippet regenerateTokenSnippet( + props: DialogRenderProps<{ loading: boolean; token: string | null }> +)} + + + {props.data.loading + ? 'Generating...' + : props.data.token + ? 'Token generated' + : 'Are you sure?'} + + {#if !props.data.loading} + + {#if props.data.token} + New token generated for {hub.name}
+ The code below will not be accessible later, please copy it now and update clients with it + {:else} + You are about to regenerate the token for {hub.name}
+ This action will invalidate the current token and disconnect clients using it
+ Are you sure you want to do this? + {/if} +
+ {/if} +
+ {#if !props.data.loading} + {#if props.data.token} +
+ New token: + +
+ + {:else} + + {/if} + {/if} +{/snippet} @@ -51,30 +207,26 @@ goto(resolve(`/hubs/${hub.id}/update`))} >Update - serializeRebootMessage($SignalR_Connection, hub.id)}> + serializeRebootMessage(getConnection(), hub.id)}> Reboot serializeEmergencyStopMessage($SignalR_Connection, hub.id)} + onclick={() => serializeEmergencyStopMessage(getConnection(), hub.id)} > Emergency Stop - serializeCaptivePortalMessage($SignalR_Connection, hub.id, true)} - > + serializeCaptivePortalMessage(getConnection(), hub.id, true)}> Enable Wi-Fi hotspot serializeCaptivePortalMessage($SignalR_Connection, hub.id, false)} + onclick={() => serializeCaptivePortalMessage(getConnection(), hub.id, false)} > Disable Wi-Fi hotspot - (pairDialogOpen = true)}>Pair - (regenerateTokenDialogOpen = true)}> - Regenerate Token - - (editDialogOpen = true)}>Edit - (deleteDialogOpen = true)}>Delete + Pair + Regenerate Token + Edit + Delete diff --git a/src/routes/(authenticated)/hubs/dialog-hub-create.svelte b/src/routes/(authenticated)/hubs/dialog-hub-create.svelte deleted file mode 100644 index bddf5815..00000000 --- a/src/routes/(authenticated)/hubs/dialog-hub-create.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - open, (o) => (open = o)}> - - - Create hub - - - - diff --git a/src/routes/(authenticated)/hubs/dialog-hub-delete.svelte b/src/routes/(authenticated)/hubs/dialog-hub-delete.svelte deleted file mode 100644 index ff09fe23..00000000 --- a/src/routes/(authenticated)/hubs/dialog-hub-delete.svelte +++ /dev/null @@ -1,40 +0,0 @@ - - - open, (o) => (open = o)}> - - - Are you sure? - - You are about to delete hub "{hub.name}"
- This will also delete the following shockers: -
- {#each hub.shockers as shocker (shocker.id)} - {shocker.name} - {/each} -
-
-
- -
-
diff --git a/src/routes/(authenticated)/hubs/dialog-hub-edit.svelte b/src/routes/(authenticated)/hubs/dialog-hub-edit.svelte deleted file mode 100644 index 43a79550..00000000 --- a/src/routes/(authenticated)/hubs/dialog-hub-edit.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - open, (o) => (open = o)}> - - - Edit hub - - - - - diff --git a/src/routes/(authenticated)/hubs/dialog-hub-pair.svelte b/src/routes/(authenticated)/hubs/dialog-hub-pair.svelte deleted file mode 100644 index f4930ad5..00000000 --- a/src/routes/(authenticated)/hubs/dialog-hub-pair.svelte +++ /dev/null @@ -1,70 +0,0 @@ - - - open, setOpen}> - -
- {loading ? 'Generating...' : code ? 'Pair code generated' : 'Generate pair code?'} - {#if !loading} - - {#if code} - Pair code generated for {hub.name}
- The code below will not be accessible later, please copy it now and update clients with it - {:else} - You are about to generate a pair code for {hub.name}
- It will be vaild for 15 minutes after its creation.
- There is only one active pair code per hub, newly generated ones will override the older active - ones.
- {/if} -
- {/if} -
- {#if !loading} - {#if code} -
- New token: - -
- - {:else} - - {/if} - {/if} -
-
diff --git a/src/routes/(authenticated)/hubs/dialog-hub-regenerate-token.svelte b/src/routes/(authenticated)/hubs/dialog-hub-regenerate-token.svelte deleted file mode 100644 index 06a6fe3e..00000000 --- a/src/routes/(authenticated)/hubs/dialog-hub-regenerate-token.svelte +++ /dev/null @@ -1,66 +0,0 @@ - - - open, setOpen}> - -
- {loading ? 'Generating...' : newToken ? 'Token generated' : 'Are you sure?'} - {#if !loading} - - {#if newToken} - New token generated for {hub.name}
- The code below will not be accessible later, please copy it now and update clients with it - {:else} - You are about to regenerate the token for {hub.name}
- This action will invalidate the current token and disconnect clients using it
- Are you sure you want to do this?
- {/if} -
- {/if} -
- {#if !loading} - {#if newToken} -
- New token: - -
- - {:else} - - {/if} - {/if} -
-
diff --git a/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/+page.svelte b/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/+page.svelte index f8369cb1..b61e0b80 100644 --- a/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/+page.svelte +++ b/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/+page.svelte @@ -8,7 +8,7 @@ import { Button } from '$lib/components/ui/button'; import * as Card from '$lib/components/ui/card'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { refreshOwnHubs } from '$lib/stores/HubsStore'; + import { refreshOwnHubs } from '$lib/stores/HubsStore.svelte'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import SharedDevice from './SharedDevice.svelte'; diff --git a/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/dialog-add-shocker.svelte b/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/dialog-add-shocker.svelte index 5af7908a..385175fa 100644 --- a/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/dialog-add-shocker.svelte +++ b/src/routes/(authenticated)/shares/public/[shareId=guid]/edit/dialog-add-shocker.svelte @@ -3,10 +3,10 @@ import Button from '$lib/components/ui/button/button.svelte'; import * as Dialog from '$lib/components/ui/dialog'; import MultiSelectCombobox from '$lib/components/ui/multi-select-combobox/multi-select-combobox.svelte'; - import { OwnHubsStore } from '$lib/stores/HubsStore'; + import { ownHubs } from '$lib/stores/HubsStore.svelte'; let availableShockers = $derived( - Array.from($OwnHubsStore) + Array.from(ownHubs) .flatMap(([, hub]) => hub.shockers) .map((shocker) => ({ value: shocker.id, diff --git a/src/routes/(authenticated)/shares/user/+layout.svelte b/src/routes/(authenticated)/shares/user/+layout.svelte index b14fd179..e3a790f2 100644 --- a/src/routes/(authenticated)/shares/user/+layout.svelte +++ b/src/routes/(authenticated)/shares/user/+layout.svelte @@ -7,7 +7,7 @@ import { Button } from '$lib/components/ui/button'; import * as Card from '$lib/components/ui/card'; import * as Tabs from '$lib/components/ui/tabs/index.js'; - import { refreshOwnHubs } from '$lib/stores/HubsStore'; + import { refreshOwnHubs } from '$lib/stores/HubsStore.svelte'; import { type Snippet, onMount } from 'svelte'; import { toast } from 'svelte-sonner'; import DialogShareCodeCreate from './dialog-share-code-create.svelte'; diff --git a/src/routes/(authenticated)/shares/user/dialog-share-code-create.svelte b/src/routes/(authenticated)/shares/user/dialog-share-code-create.svelte index fe4b4d3f..cbdb2f42 100644 --- a/src/routes/(authenticated)/shares/user/dialog-share-code-create.svelte +++ b/src/routes/(authenticated)/shares/user/dialog-share-code-create.svelte @@ -8,11 +8,11 @@ import * as Dialog from '$lib/components/ui/dialog'; import MultiSelectCombobox from '$lib/components/ui/multi-select-combobox/multi-select-combobox.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { OwnHubsStore } from '$lib/stores/HubsStore'; + import { ownHubs } from '$lib/stores/HubsStore.svelte'; import { refreshOutgoingInvites } from '$lib/stores/UserSharesStore'; let availableShockers = $derived( - Array.from($OwnHubsStore) + Array.from(ownHubs) .flatMap(([, hub]) => hub.shockers) .map((shocker) => ({ value: shocker.id, diff --git a/src/routes/(authenticated)/shares/user/incoming/incoming-share-item.svelte b/src/routes/(authenticated)/shares/user/incoming/incoming-share-item.svelte index 3cb492eb..59f6bb09 100644 --- a/src/routes/(authenticated)/shares/user/incoming/incoming-share-item.svelte +++ b/src/routes/(authenticated)/shares/user/incoming/incoming-share-item.svelte @@ -8,7 +8,7 @@ import * as Table from '$lib/components/ui/table'; import * as Tooltip from '$lib/components/ui/tooltip'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { openConfirmDialog } from '$lib/stores/ConfirmDialogStore'; + import { dialog } from '$lib/components/dialog-manager/dialog-store.svelte'; import { refreshOutgoingInvites } from '$lib/stores/UserSharesStore'; import { toast } from 'svelte-sonner'; @@ -29,14 +29,14 @@ } } - function removeShare() { - openConfirmDialog({ + async function removeShare() { + const result = await dialog.confirm({ title: 'Remove Share', confirmButtonText: 'Remove', data: share, - onConfirm: removeShareCall, descSnippet: confirmDesc, }); + if (result.confirmed) await removeShareCall(result.data); } diff --git a/src/routes/(authenticated)/shares/user/incoming/manage-share.svelte b/src/routes/(authenticated)/shares/user/incoming/manage-share.svelte index 13366eff..faa08375 100644 --- a/src/routes/(authenticated)/shares/user/incoming/manage-share.svelte +++ b/src/routes/(authenticated)/shares/user/incoming/manage-share.svelte @@ -7,7 +7,7 @@ import * as Drawer from '$lib/components/ui/drawer'; import * as Table from '$lib/components/ui/table/index.js'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { openConfirmDialog } from '$lib/stores/ConfirmDialogStore'; + import { dialog } from '$lib/components/dialog-manager/dialog-store.svelte'; import { UserShares, refreshUserShares } from '$lib/stores/UserSharesStore'; import { UserStore } from '$lib/stores/UserStore'; import { toast } from 'svelte-sonner'; @@ -36,14 +36,14 @@ } } - function handleDeleteClick(shocker: UserShareInfo) { - openConfirmDialog({ + async function handleDeleteClick(shocker: UserShareInfo) { + const result = await dialog.confirm({ title: 'Confirm removal of Incoming Shocker Share', descSnippet: deleteConfirmDesc, data: shocker, - onConfirm: deleteShockerShare, confirmButtonText: 'Remove Share', }); + if (result.confirmed) await deleteShockerShare(result.data); } diff --git a/src/routes/(authenticated)/shares/user/invites/incoming-invite-item.svelte b/src/routes/(authenticated)/shares/user/invites/incoming-invite-item.svelte index e72f1140..6dbda63d 100644 --- a/src/routes/(authenticated)/shares/user/invites/incoming-invite-item.svelte +++ b/src/routes/(authenticated)/shares/user/invites/incoming-invite-item.svelte @@ -9,7 +9,7 @@ import * as Table from '$lib/components/ui/table'; import * as Tooltip from '$lib/components/ui/tooltip'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { openConfirmDialog } from '$lib/stores/ConfirmDialogStore'; + import { dialog } from '$lib/components/dialog-manager/dialog-store.svelte'; import { refreshIncomingInvites } from '$lib/stores/UserSharesStore'; import { cn } from '$lib/utils'; import { toast } from 'svelte-sonner'; @@ -42,14 +42,14 @@ } } - function denyInvite() { - openConfirmDialog({ + async function denyInvite() { + const result = await dialog.confirm({ title: 'Deny Invite', confirmButtonText: 'Deny', data: shareInvite, - onConfirm: denyInviteCall, descSnippet: confirmDesc, }); + if (result.confirmed) await denyInviteCall(result.data); } diff --git a/src/routes/(authenticated)/shares/user/invites/outgoing-invite-item.svelte b/src/routes/(authenticated)/shares/user/invites/outgoing-invite-item.svelte index 0467d907..9ae266c5 100644 --- a/src/routes/(authenticated)/shares/user/invites/outgoing-invite-item.svelte +++ b/src/routes/(authenticated)/shares/user/invites/outgoing-invite-item.svelte @@ -9,7 +9,7 @@ import * as Table from '$lib/components/ui/table'; import * as Tooltip from '$lib/components/ui/tooltip'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { openConfirmDialog } from '$lib/stores/ConfirmDialogStore'; + import { dialog } from '$lib/components/dialog-manager/dialog-store.svelte'; import { refreshOutgoingInvites } from '$lib/stores/UserSharesStore'; import { cn } from '$lib/utils'; import { toast } from 'svelte-sonner'; @@ -40,14 +40,14 @@ } } - function removeInvite() { - openConfirmDialog({ + async function removeInvite() { + const result = await dialog.confirm({ title: 'Cancel Invite', confirmButtonText: 'Cancel', data: shareInvite, - onConfirm: removeInviteCall, descSnippet: confirmDesc, }); + if (result.confirmed) await removeInviteCall(result.data); } diff --git a/src/routes/(authenticated)/shares/user/outgoing/edit-share.svelte b/src/routes/(authenticated)/shares/user/outgoing/edit-share.svelte index f10e5fbd..e10c233b 100644 --- a/src/routes/(authenticated)/shares/user/outgoing/edit-share.svelte +++ b/src/routes/(authenticated)/shares/user/outgoing/edit-share.svelte @@ -12,7 +12,7 @@ import MultiPauseToggle from '$lib/components/utils/MultiPauseToggle.svelte'; import PauseToggle from '$lib/components/utils/PauseToggle.svelte'; import { handleApiError } from '$lib/errorhandling/apiErrorHandling'; - import { openConfirmDialog } from '$lib/stores/ConfirmDialogStore'; + import { dialog } from '$lib/components/dialog-manager/dialog-store.svelte'; import { UserShares, refreshUserShares } from '$lib/stores/UserSharesStore'; import { onMount } from 'svelte'; import { toast } from 'svelte-sonner'; @@ -166,14 +166,14 @@ } } - function handleDeleteClick(shocker: EditableShare) { - openConfirmDialog({ + async function handleDeleteClick(shocker: EditableShare) { + const result = await dialog.confirm({ title: 'Confirm Deletion', descSnippet: deleteConfirmDesc, data: shocker, - onConfirm: deleteShockerShare, confirmButtonText: 'Remove', }); + if (result.confirmed) await deleteShockerShare(result.data); } function onTabChanged(value: string) { diff --git a/src/routes/(authenticated)/shockers/own/+page.svelte b/src/routes/(authenticated)/shockers/own/+page.svelte index 7a308d72..df698a72 100644 --- a/src/routes/(authenticated)/shockers/own/+page.svelte +++ b/src/routes/(authenticated)/shockers/own/+page.svelte @@ -11,10 +11,10 @@ import { Button } from '$lib/components/ui/button'; import * as Popover from '$lib/components/ui/popover'; import { ControlDurationDefault, ControlIntensityDefault } from '$lib/constants/ControlConstants'; - import { OwnHubsStore, refreshOwnHubs } from '$lib/stores/HubsStore'; + import { ownHubs, refreshOwnHubs } from '$lib/stores/HubsStore.svelte'; import { onMount } from 'svelte'; - let shockers = $derived(Array.from($OwnHubsStore).flatMap(([, hub]) => hub.shockers)); + let shockers = $derived(Array.from(ownHubs).flatMap(([, hub]) => hub.shockers)); let moduleType = $state(ModuleType.ClassicControlModule); @@ -25,7 +25,7 @@ onMount(refreshOwnHubs); -{#if $OwnHubsStore == null} +{#if ownHubs.size === 0}

Loading...

{:else} diff --git a/src/routes/(authenticated)/shockers/shared/+page.svelte b/src/routes/(authenticated)/shockers/shared/+page.svelte index 39adbb24..cecd6727 100644 --- a/src/routes/(authenticated)/shockers/shared/+page.svelte +++ b/src/routes/(authenticated)/shockers/shared/+page.svelte @@ -3,7 +3,7 @@ import Container from '$lib/components/Container.svelte'; import SharedShockerControlModule from '$lib/components/ControlModules/SharedShockerControlModule.svelte'; import * as Avatar from '$lib/components/ui/avatar'; - import { OnlineHubsStore } from '$lib/stores/HubsStore'; + import { onlineHubs } from '$lib/stores/HubsStore.svelte'; import { SharedHubsStore, refreshSharedHubs } from '$lib/stores/SharedHubsStore'; import { onMount } from 'svelte'; @@ -57,7 +57,7 @@
{device.name} - {#if $OnlineHubsStore.get(device.id)?.isOnline} + {#if onlineHubs.get(device.id)?.isOnline} Online diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index abec3386..ce9a78b1 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -11,8 +11,8 @@ import Sidebar from './Sidebar.svelte'; import '../app.css'; import { browser } from '$app/environment'; - import DialogManager from '$lib/components/confirm-dialog/dialog-manager.svelte'; import { isMobile } from '$lib/utils/compatibility'; + import DialogManager from '$lib/components/dialog-manager/dialog-manager.svelte'; interface Props { children?: Snippet; diff --git a/src/routes/Footer.svelte b/src/routes/Footer.svelte index f236081e..22ab6b9a 100644 --- a/src/routes/Footer.svelte +++ b/src/routes/Footer.svelte @@ -3,7 +3,7 @@ import { HubConnectionState } from '@microsoft/signalr'; import { PUBLIC_GITHUB_PROJECT_URL } from '$env/static/public'; - import { SignalR_State } from '$lib/signalr'; + import { getConnectionState } from '$lib/signalr/index.svelte'; import { Wifi, WifiOff } from '@lucide/svelte'; import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js'; import { Button } from '$lib/components/ui/button'; @@ -26,7 +26,7 @@ {#snippet child({ props })}