- Refactor groupDispatchers to accept userId as a parameter instead of closing over nullable userId
- Replace mutationGroup! with a guard clause
- Use optional chaining for preferenceChildrenOptionsByKey lookup with fallback
- Clear optimistic store on user/session change to prevent stale values leaking across logins
- Move dispatcher lookup before setOptimistic to avoid permanent ghost entries when no dispatcher exists
- Inline dayjs.weekdays() calls to reflect runtime locale changes instead of caching at module load
- Remove unused UserPreferenceKey import
Replace 5 per-preference children files with a single
preference-registry.tsx that auto-generates CMDK children via factory
functions. Rewrite useUserPreferences from ~347 lines to ~160 lines by
deriving mutation dispatch, pending state, and optimistic values from
preference definitions.
Simplify settings-children.tsx and groups.tsx to iterate over
visiblePreferenceDefinitions instead of maintaining manual mappings.
Deleted: board.tsx, color-scheme.tsx, first-day-of-week.tsx,
search-engine.tsx, registry.ts
Net reduction: ~261 lines across 9 files.
Remove WidgetEmptyState guard from downloads (widget renders fine with
empty data). Fix columnsSort filter logic from .some() to .every() so
non-sortable columns are correctly excluded. Add server-side prefetch
via tRPC caller so download data is available immediately on page load.
Add WidgetEmptyState component for widgets that need a loading fallback.
Disable refetch-on-mount/focus/reconnect and retry for integration widgets
so cached data is preserved. Ensure hooks are never called conditionally.
Enrich userPreferenceDefinitions with mutationGroup field to express
which tRPC mutation handles each preference key. Move
visiblePreferenceDefinitions helper from spotlight registry to the
shared settings package.
This enables data-driven mutation dispatch in the CMDK hook and
eliminates manual key-to-group mappings.
Replace individual Redis keys (queryCache:sha256(...)) with a Hash per
user+board (qc:{userId}:{boardId}). This enables HGETALL to read all
cached entries in a single call for server-side hydration. Remove unused
SHA256 hashKey function and createHash import.
Add tRPC router (queryCache.getItem/setItem/removeItem) for persisting
TanStack Query cache entries to Redis, scoped per user and board.
Includes isPersistableWidgetQueryKey filter that excludes widget.app.ping,
and comprehensive tests covering access control, TTL expiry, and size limits.
- Add isFetched guard in media-requests/list to avoid throwing before data loads
- Scope refresh indicator to only track widget and integration queries
- Move hooks above early returns in system-disks, immich-carousel, media-transcoding
- Add enabled flag to immich-carousel query when albumId is absent
- Replace disconnected local state in ping-indicator with query cache setData
- Stabilize docker timestamp with useMemo to prevent useTimeAgo flicker
- Wrap releases results in useMemo with proper type narrowing
- Hide refresh indicator during edit mode
Non-blocking data fetching for all 38 widget components. Widgets now
render immediately and show data when available, with loading state
delegated to the global refresh indicator in the board header.
Replace per-widget loading spinners with a shared header indicator that
shows live TanStack Query cache status for all widget-related queries.
Includes orange/green dot, completion beep, and hover card with
per-query staleness and error details.
Discovered Docker apps now use the same buildAppUrl logic as
integrations when a base host is set, instead of using the raw
Docker socket URL (e.g. http://container:5432).
Extracts shared buildUrl helper and adds buildAppUrl that derives
a slug from the container name. Also passes publishedPort through
the DiscoveredApp type so host:port mode works correctly.
Subdomain-based URLs (e.g. sonarr.example.com) are typically served
behind a reverse proxy with TLS. Update the template to generate
https:// URLs. Host:port mode remains http:// for local access.
- Replace plain Base URL input with a prominent Alert callout so users
don't overlook it
- Add disableScroll prop to SelectGridLayout and use it in onboarding
so integrations expand the page instead of scrolling
- Fix Collapse `in` → `expanded` prop for Mantine v9 migration in both
the URL helpers panel and the Docker discovery panel
Replace per-step hardcoded Card widths (384px, 776px, 1024px) with a
consistent 1024px shell defined in _constants.ts. All steps now use an
InitStepCard wrapper set to w=100%, inheriting width from the page shell.
Also updates old-import cards to w=100% for proper width propagation.