diff --git a/src/modules/toast/application/toastSettings.ts b/src/modules/toast/application/toastSettings.ts new file mode 100644 index 000000000..ef2f8d94a --- /dev/null +++ b/src/modules/toast/application/toastSettings.ts @@ -0,0 +1,34 @@ +import { type ToastSettings } from '~/modules/toast/domain/toastSettings' +import { createToastSettingsStore } from '~/modules/toast/infrastructure/toastSettings' + +const toastSettingsStore = createToastSettingsStore() + +/** + * Gets the current toast settings from the default runtime store. + * + * @returns The current toast settings. + */ +export function getToastSettings(): ToastSettings { + return toastSettingsStore.getToastSettings() +} + +/** + * Updates the current toast settings in the default runtime store. + * + * @param updates Partial toast settings to apply. + * @returns void. + */ +export function updateToastSettings(updates: Partial): void { + toastSettingsStore.updateToastSettings(updates) +} + +/** + * Resets the current toast settings in the default runtime store. + * + * @returns void. + */ +export function resetToastSettings(): void { + toastSettingsStore.resetToastSettings() +} + +export type { ToastSettings } diff --git a/src/modules/toast/domain/toastSettings.ts b/src/modules/toast/domain/toastSettings.ts new file mode 100644 index 000000000..5a92fb6f8 --- /dev/null +++ b/src/modules/toast/domain/toastSettings.ts @@ -0,0 +1,17 @@ +/** + * User-configurable toast settings. + */ +export type ToastSettings = { + /** Show success toasts for background operations */ + showBackgroundSuccess: boolean + /** Show loading toasts for background operations */ + showBackgroundLoading: boolean + /** Automatically dismiss error toasts */ + autoDismissErrors: boolean + /** Default duration for toasts in milliseconds */ + defaultDuration: number + /** Group similar toasts together */ + groupSimilarToasts: boolean + /** Show detailed error information in toasts */ + showDetailedErrors: boolean +} diff --git a/src/modules/toast/infrastructure/toastSettings.ts b/src/modules/toast/infrastructure/toastSettings.ts index 6c4e945eb..a70b2eed9 100644 --- a/src/modules/toast/infrastructure/toastSettings.ts +++ b/src/modules/toast/infrastructure/toastSettings.ts @@ -5,34 +5,11 @@ * Settings are persisted in local storage. */ -import { createEffect, createSignal } from 'solid-js' +import { createRoot, createSignal } from 'solid-js' +import { type ToastSettings } from '~/modules/toast/domain/toastSettings' import { jsonParseWithStack } from '~/shared/utils/jsonParseWithStack' -/** - * User-configurable toast settings. - * @property showBackgroundSuccess Whether to show success toasts for background operations - * @property showBackgroundLoading Whether to show loading toasts for background operations - * @property autoDismissErrors Whether to automatically dismiss error toasts - * @property defaultDuration Default duration for toasts in milliseconds - * @property groupSimilarToasts Whether to group similar toasts together - * @property showDetailedErrors Whether to show detailed error information in toasts - */ -export type ToastSettings = { - /** Show success toasts for background operations */ - showBackgroundSuccess: boolean - /** Show loading toasts for background operations */ - showBackgroundLoading: boolean - /** Automatically dismiss error toasts */ - autoDismissErrors: boolean - /** Default duration for toasts in milliseconds */ - defaultDuration: number - /** Group similar toasts together */ - groupSimilarToasts: boolean - /** Show detailed error information in toasts */ - showDetailedErrors: boolean -} - /** * Default toast settings */ @@ -48,11 +25,15 @@ const DEFAULT_SETTINGS: ToastSettings = { // Local storage key for persisting settings const STORAGE_KEY = 'macroflows:toast-settings' +type ToastSettingsStoreConfig = { + storageKey?: string +} + /** * Load settings from local storage */ -function loadSettings(): ToastSettings { - const stored = localStorage.getItem(STORAGE_KEY) +function loadSettings(storageKey: string): ToastSettings { + const stored = localStorage.getItem(storageKey) if (stored !== null && stored.length > 0) { try { const parsed = jsonParseWithStack(stored) @@ -69,63 +50,44 @@ function loadSettings(): ToastSettings { return { ...DEFAULT_SETTINGS } } -// Create reactive signal for settings -const [settings, setSettings] = createSignal(loadSettings()) - -// Persist settings to local storage when they change -createEffect(() => { - localStorage.setItem(STORAGE_KEY, JSON.stringify(settings())) -}) - -/** - * Gets the current toast settings. - * @returns The current ToastSettings object. - */ -export function getToastSettings(): ToastSettings { - return settings() -} - -/** - * Updates toast settings. - * @param updates Partial settings to update. - */ -export function updateToastSettings(updates: Partial): void { - setSettings((current) => ({ ...current, ...updates })) -} - -/** - * Resets toast settings to defaults. - */ -export function resetToastSettings(): void { - setSettings({ ...DEFAULT_SETTINGS }) +function persistSettings(storageKey: string, settings: ToastSettings): void { + localStorage.setItem(storageKey, JSON.stringify(settings)) } /** - * Helper functions for specific settings + * Factory that creates a toast settings store backed by localStorage. + * + * @param config Optional storage configuration for alternate wiring or tests. + * @returns A store with read, update, and reset operations for toast settings. */ +export function createToastSettingsStore(config?: ToastSettingsStoreConfig) { + const storageKey = config?.storageKey ?? STORAGE_KEY -export function setShowBackgroundSuccess(value: boolean): void { - updateToastSettings({ showBackgroundSuccess: value }) -} - -export function setShowBackgroundLoading(value: boolean): void { - updateToastSettings({ showBackgroundLoading: value }) -} + return createRoot(() => { + const initialSettings = loadSettings(storageKey) + const [settings, setSettings] = createSignal(initialSettings) + persistSettings(storageKey, initialSettings) -export function setAutoDismissErrors(value: boolean): void { - updateToastSettings({ autoDismissErrors: value }) -} + function getToastSettings(): ToastSettings { + return settings() + } -export function setDefaultDuration(value: number): void { - updateToastSettings({ defaultDuration: value }) -} + function updateToastSettings(updates: Partial): void { + const nextSettings = { ...settings(), ...updates } + setSettings(nextSettings) + persistSettings(storageKey, nextSettings) + } -export function setGroupSimilarToasts(value: boolean): void { - updateToastSettings({ groupSimilarToasts: value }) -} + function resetToastSettings(): void { + const nextSettings = { ...DEFAULT_SETTINGS } + setSettings(nextSettings) + persistSettings(storageKey, nextSettings) + } -export function setShowDetailedErrors(value: boolean): void { - updateToastSettings({ showDetailedErrors: value }) + return { + getToastSettings, + updateToastSettings, + resetToastSettings, + } + }) } - -// Add JSDoc for all exported types and functions for better maintainability diff --git a/src/modules/toast/tests/toastSettings.test.ts b/src/modules/toast/tests/toastSettings.test.ts index 6f2910406..bd701461d 100644 --- a/src/modules/toast/tests/toastSettings.test.ts +++ b/src/modules/toast/tests/toastSettings.test.ts @@ -7,14 +7,9 @@ vi.mock('~/shared/config/env', () => ({ import { getToastSettings, resetToastSettings, - setAutoDismissErrors, - setDefaultDuration, - setGroupSimilarToasts, - setShowBackgroundLoading, - setShowBackgroundSuccess, - setShowDetailedErrors, updateToastSettings, -} from '~/modules/toast/infrastructure/toastSettings' +} from '~/modules/toast/application/toastSettings' +import { createToastSettingsStore } from '~/modules/toast/infrastructure/toastSettings' const DEFAULTS = { showBackgroundSuccess: false, @@ -26,6 +21,8 @@ const DEFAULTS = { } const STORAGE_KEY = 'macroflows:toast-settings' +const FIRST_STORAGE_KEY = 'macroflows:toast-settings:first' +const SECOND_STORAGE_KEY = 'macroflows:toast-settings:second' let localStorageMock: Record = {} @@ -67,19 +64,28 @@ describe('toastSettings', () => { expect(getToastSettings().showBackgroundSuccess).toBe(true) }) - it('setters update only their value', () => { - setShowBackgroundSuccess(true) - expect(getToastSettings().showBackgroundSuccess).toBe(true) - setShowBackgroundLoading(true) - expect(getToastSettings().showBackgroundLoading).toBe(true) - setAutoDismissErrors(true) - expect(getToastSettings().autoDismissErrors).toBe(true) - setDefaultDuration(9999) - expect(getToastSettings().defaultDuration).toBe(9999) - setGroupSimilarToasts(false) - expect(getToastSettings().groupSimilarToasts).toBe(false) - setShowDetailedErrors(false) - expect(getToastSettings().showDetailedErrors).toBe(false) + it('createToastSettingsStore keeps persistence isolated per storage key', () => { + const firstStore = createToastSettingsStore({ + storageKey: FIRST_STORAGE_KEY, + }) + const secondStore = createToastSettingsStore({ + storageKey: SECOND_STORAGE_KEY, + }) + + expect(localStorageMock[FIRST_STORAGE_KEY]).toBe(JSON.stringify(DEFAULTS)) + expect(localStorageMock[SECOND_STORAGE_KEY]).toBe(JSON.stringify(DEFAULTS)) + + firstStore.updateToastSettings({ showBackgroundSuccess: true }) + + expect(firstStore.getToastSettings().showBackgroundSuccess).toBe(true) + expect(secondStore.getToastSettings()).toEqual(DEFAULTS) + expect(localStorageMock[FIRST_STORAGE_KEY]).toBe( + JSON.stringify({ + ...DEFAULTS, + showBackgroundSuccess: true, + }), + ) + expect(localStorageMock[SECOND_STORAGE_KEY]).toBe(JSON.stringify(DEFAULTS)) }) it('resetToastSettings restores defaults', () => { @@ -97,7 +103,7 @@ describe('toastSettings', () => { vi.resetModules() setMockLocalStorage() const toastSettingsModule = - await import('~/modules/toast/infrastructure/toastSettings') + await import('~/modules/toast/application/toastSettings') expect(toastSettingsModule.getToastSettings().showBackgroundSuccess).toBe( true, ) @@ -109,7 +115,7 @@ describe('toastSettings', () => { vi.resetModules() setMockLocalStorage() const toastSettingsModule = - await import('~/modules/toast/infrastructure/toastSettings') + await import('~/modules/toast/application/toastSettings') expect(toastSettingsModule.getToastSettings()).toEqual(DEFAULTS) }) }) diff --git a/src/sections/settings/components/ToastSettings.tsx b/src/sections/settings/components/ToastSettings.tsx index b94c57786..f52943ed3 100644 --- a/src/sections/settings/components/ToastSettings.tsx +++ b/src/sections/settings/components/ToastSettings.tsx @@ -9,7 +9,7 @@ import { getToastSettings, resetToastSettings, updateToastSettings, -} from '~/modules/toast/infrastructure/toastSettings' +} from '~/modules/toast/application/toastSettings' export function ToastSettings() { const [settings, setSettings] = createSignal(getToastSettings())