Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/modules/toast/application/toastSettings.ts
Original file line number Diff line number Diff line change
@@ -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<ToastSettings>): 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 }
17 changes: 17 additions & 0 deletions src/modules/toast/domain/toastSettings.ts
Original file line number Diff line number Diff line change
@@ -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
}
116 changes: 39 additions & 77 deletions src/modules/toast/infrastructure/toastSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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)
Expand All @@ -69,63 +50,44 @@ function loadSettings(): ToastSettings {
return { ...DEFAULT_SETTINGS }
}

// Create reactive signal for settings
const [settings, setSettings] = createSignal<ToastSettings>(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<ToastSettings>): 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<ToastSettings>(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<ToastSettings>): 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
50 changes: 28 additions & 22 deletions src/modules/toast/tests/toastSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<string, string> = {}

Expand Down Expand Up @@ -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', () => {
Expand All @@ -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,
)
Expand All @@ -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)
})
})
2 changes: 1 addition & 1 deletion src/sections/settings/components/ToastSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Loading