This commit includes comprehensive codebase cleanup and refactoring: ## Code Cleanup - Remove dead TypeScript code (types/monitoring.ts - 194 lines duplicate) - Remove unused Go functions (GetClusterNodes, MigratePassword, GetClusterHealthInfo) - Clean up commented-out code blocks across multiple files - Remove unused TypeScript exports (helpTextClass, private tag color helpers) - Delete obsolete test files and components ## localStorage Consolidation - Centralize all storage keys into STORAGE_KEYS constant - Update 5 files to use centralized keys: * utils/apiClient.ts (AUTH, LEGACY_TOKEN) * components/Dashboard/Dashboard.tsx (GUEST_METADATA) * components/Docker/DockerHosts.tsx (DOCKER_METADATA) * App.tsx (PLATFORMS_SEEN) * stores/updates.ts (UPDATES) - Benefits: Single source of truth, prevents typos, better maintainability ## Previous Work Committed - Docker monitoring improvements and disk metrics - Security enhancements and setup fixes - API refactoring and cleanup - Documentation updates - Build system improvements ## Testing - All frontend tests pass (29 tests) - All Go tests pass (15 packages) - Production build successful - Zero breaking changes Total: 186 files changed, 5825 insertions(+), 11602 deletions(-)
75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
import { Accessor, Setter, createEffect, createSignal } from 'solid-js';
|
|
import { logger } from '@/utils/logger';
|
|
|
|
export type PersistentSignalOptions<T> = {
|
|
/**
|
|
* Custom serialization function. Defaults to `String(value)`.
|
|
*/
|
|
serialize?: (value: T) => string;
|
|
/**
|
|
* Custom deserialization function. Defaults to casting the stored string.
|
|
*/
|
|
deserialize?: (value: string) => T;
|
|
/**
|
|
* Optional equality comparison passed to Solid's `createSignal`.
|
|
*/
|
|
equals?: (prev: T, next: T) => boolean;
|
|
/**
|
|
* Alternate storage implementation (defaults to `window.localStorage`).
|
|
*/
|
|
storage?: Storage;
|
|
};
|
|
|
|
/**
|
|
* Creates a Solid signal that persists its value to localStorage (or a custom storage).
|
|
* The signal reads the initial value synchronously from storage when available.
|
|
*/
|
|
export function usePersistentSignal<T>(
|
|
key: string,
|
|
defaultValue: T,
|
|
options: PersistentSignalOptions<T> = {},
|
|
): [Accessor<T>, Setter<T>] {
|
|
const storage =
|
|
options.storage ?? (typeof window !== 'undefined' ? window.localStorage : undefined);
|
|
const serialize = options.serialize ?? ((value: T) => String(value));
|
|
const deserialize = options.deserialize ?? ((value: string) => value as unknown as T);
|
|
|
|
const initialValue = (() => {
|
|
if (!storage) {
|
|
return defaultValue;
|
|
}
|
|
|
|
try {
|
|
const raw = storage.getItem(key);
|
|
if (raw === null) {
|
|
return defaultValue;
|
|
}
|
|
return deserialize(raw);
|
|
} catch (err) {
|
|
logger.warn(`[usePersistentSignal] Failed to read "${key}" from storage`, err);
|
|
return defaultValue;
|
|
}
|
|
})();
|
|
|
|
const signalOptions = options.equals ? { equals: options.equals } : undefined;
|
|
const [value, setValue] = createSignal<T>(initialValue, signalOptions);
|
|
|
|
createEffect(() => {
|
|
if (!storage) {
|
|
return;
|
|
}
|
|
|
|
const current = value();
|
|
try {
|
|
if (current === undefined || current === null) {
|
|
storage.removeItem(key);
|
|
} else {
|
|
storage.setItem(key, serialize(current));
|
|
}
|
|
} catch (err) {
|
|
logger.warn(`[usePersistentSignal] Failed to persist "${key}"`, err);
|
|
}
|
|
});
|
|
|
|
return [value, setValue];
|
|
}
|