Theme System Flash-Persistence Integration

Complete guide to the theme system's flash-persistence implementation for instant load and cross-tab sync

⚡ Theme System Flash-Persistence Integration

Overview

The GoGrab Space theme system has been fully integrated with the flash-persistence system from @storage-system, providing:


What Changed

1. ThemeState Service

Location: libs/theme-system/theme-runtime/src/lib/state/theme.state.ts

Before:

private state = autoFlashState<ThemeStateModel>('theme-state', {
  currentTheme: null,
  availableThemes: [],
  // ...
});

After:

private state = autoFlashState<ThemeStateModel>('theme-state', {
  currentTheme: null,
  availableThemes: [],
  // ...
}, {
  database: 'theme-system',          // Dedicated database
  collection: 'state',                // Organized collection
  priority: 'critical',               // Instant sync
  ttl: 30 * 24 * 60 * 60 * 1000,     // 30 days
  debounceMs: 100                     // Quick persistence
});

Benefits:


2. ThemeStateManager Service

Location: libs/theme-system/theme-runtime/src/lib/services/theme-state-manager.service.ts

Changes:

Result:


3. SimpleThemeStorageService

Location: libs/theme-system/theme-runtime/src/lib/services/simple-theme-storage.service.ts

New Flash-Persisted Signals:

// Active Theme ID - instant sync
readonly activeThemeId = autoFlashState<string | null>('active-theme-id', null, {
  database: 'theme-system',
  collection: 'preferences',
  priority: 'critical',              // Instant sync - theme is critical UX
  ttl: 90 * 24 * 60 * 60 * 1000,    // 90 days
  debounceMs: 50                     // Very fast persistence
});

// Theme Settings - user preferences
readonly themeSettings = autoFlashState<Record<string, any>>('theme-settings', {}, {
  database: 'theme-system',
  collection: 'preferences',
  priority: 'high',                  // High priority
  ttl: 90 * 24 * 60 * 60 * 1000,    // 90 days
  debounceMs: 300                    // Normal debounce
});

Storage Configuration Enhanced:

private readonly storage = GgStorage.create({
  namespace: 'theme-system',
  strategy: 'speed',
  performance: {
    microCacheBudget: 100 * 1024,
    redundancy: true  // ✨ NEW: Enable redundancy for critical theme data
  }
});

Benefits:


4. ThemeDesignerComponent

Location: libs/libs-space/theme-designer/src/lib/theme-designer/theme-designer.component.ts

New Flash-Persisted State:

// Preview mode - persisted UI state
previewMode = autoFlashState<'quick' | 'detailed'>('preview-mode', 'quick', {
  database: 'theme-system',
  collection: 'designer-ui',
  priority: 'normal',
  ttl: 7 * 24 * 60 * 60 * 1000,  // 7 days - UI preference
  debounceMs: 500
});

// Theme changes count - session continuity
themeChanges = autoFlashState<number>('theme-changes-count', 0, {
  database: 'theme-system',
  collection: 'designer-session',
  priority: 'low',
  ttl: 24 * 60 * 60 * 1000,      // 24 hours
  debounceMs: 1000
});

Benefits:


5. ThemeEditorComponent

Location: libs/theme-system/theme-dev-tools/src/lib/components/theme-editor/theme-editor.component.ts

Key Updates:

// Use flash-persisted active theme ID
readonly activeThemeId = this.themeStorage.activeThemeId;

// Show save feedback
readonly saveFeedback = signal<string | null>(null);

Method Updates:

UI Enhancements:

<!-- Save feedback display -->
<div *ngIf="saveFeedback()" class="save-feedback">
  {{ saveFeedback() }}
</div>

<!-- Enhanced button labels -->
<button>💾 Save & Apply</button>
<button>⚡ Set Active</button>
<button>📋 Clone</button>

Benefits:


Storage Key Structure

The theme system uses a hierarchical key structure for organization:

[database]:[namespace]:[collection]:[key]

Theme System Keys

Key Description Priority TTL
theme-system:flash:state:theme-state Main theme state Critical 30 days
theme-system:flash:runtime:theme-runtime-state Runtime state Critical 30 days
theme-system:flash:preferences:active-theme-id Active theme ID Critical 90 days
theme-system:flash:preferences:theme-settings Theme settings High 90 days
theme-system:flash:designer-ui:preview-mode UI preferences Normal 7 days
theme-system:flash:designer-session:theme-changes-count Session data Low 24 hours

Performance Metrics

Before Flash-Persistence

After Flash-Persistence


Developer Usage

Accessing Active Theme ID

import { inject } from '@angular/core';
import { SimpleThemeStorageService } from '@theme-runtime';

export class MyComponent {
  private themeStorage = inject(SimpleThemeStorageService);
  
  // ✨ Reactive signal - updates automatically
  activeThemeId = this.themeStorage.activeThemeId;
  
  // Set new active theme (instant + cross-tab sync)
  setTheme(themeId: string) {
    this.activeThemeId.set(themeId);
  }
}

Accessing Theme State

import { inject } from '@angular/core';
import { ThemeState } from '@theme-runtime';

export class MyComponent {
  private themeState = inject(ThemeState);
  
  // ✨ Computed selectors (reactive)
  themes = this.themeState.themes;           // Available themes
  activeTheme = this.themeState.activeTheme; // Current theme
  isLoading = this.themeState.isLoading;     // Loading state
}

Creating Custom Flash-Persisted Theme State

import { autoFlashState } from '@storage-system';

export class ThemeCustomizationComponent {
  // ✨ Auto-persisted user customizations
  customColors = autoFlashState('custom-colors', {}, {
    database: 'theme-system',
    collection: 'user-customizations',
    priority: 'high',
    ttl: 90 * 24 * 60 * 60 * 1000
  });
  
  saveColor(key: string, value: string) {
    this.customColors.update(colors => ({
      ...colors,
      [key]: value
    }));
    // ✨ Automatically persisted!
  }
}

Migration from Old System

Old Approach (Manual Persistence)

// ❌ Manual async persistence
async setActiveTheme(themeId: string) {
  await this.storage.setActiveThemeId(themeId);
  await this.storage.saveToLocalStorage();
  this.broadcastToOtherTabs(themeId);
}

New Approach (Flash-Persistence)

// ✅ Instant synchronous update + automatic persistence
setActiveTheme(themeId: string) {
  this.activeThemeId.set(themeId);
  // Done! Flash-persistence handles:
  // - Instant UI update
  // - Background persistence
  // - Cross-tab synchronization
}

Benefits Summary

For Users

For Developers


Testing Flash-Persistence

Test Instant Load

  1. Set a theme in the theme designer
  2. Hard refresh the page (Cmd/Ctrl + Shift + R)
  3. Theme should load instantly (< 1ms), no flash of default theme

Test Cross-Tab Sync

  1. Open two browser tabs with gograb-space
  2. Change theme in one tab
  3. Other tab should update instantly (< 50ms)

Test Persistence Duration

  1. Set a theme
  2. Close browser completely
  3. Reopen after 1 day, 7 days, 30 days, 90 days
  4. Theme should persist according to TTL:
    • UI preferences: 7 days
    • Session data: 24 hours
    • Theme state: 30 days
    • User preferences: 90 days

Troubleshooting

Theme Not Persisting

Check storage system is initialized:

const storage = inject(GgStorage);
const capabilities = storage.getCapabilities();
console.log('Storage available:', capabilities);

Check flash-persistence is working:

import { getAutoDiscoveredStates } from '@storage-system';
console.log('Flash states:', getAutoDiscoveredStates());

Cross-Tab Sync Not Working

Verify BroadcastChannel support:

if ('BroadcastChannel' in window) {
  console.log('✅ Cross-tab sync supported');
} else {
  console.log('❌ BroadcastChannel not available');
}

Check storage registry:

const registry = inject(StorageRegistryService);
console.log('Active adapters:', registry.getActiveAdapters());

Related Documentation


Future Enhancements


Last Updated: November 21, 2025 - 6:45 PM
Session ID: 11.25.2.1
Status: ✅ Complete and Production-Ready