PAGE PERSISTENCE FLOW

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

📄 Page Persistence Flow - Complete Guide

Date: November 5, 2025
Status: ✅ Documented & Fixed
Purpose: Explain where and how pages are persisted when created in the Editor


🎯 The Complete Flow

When You Navigate to /editor (Blank Canvas):

1. User navigates to: /space/{space}/editor
   ↓
2. EditorWorkspaceComponent loads
   ↓
3. No pageId in route → context = 'blank-canvas'
   ↓
4. User adds widgets to canvas
   ↓
5. EditorService.setWidgets() called
   ↓
6. ❓ No page exists → Creates temporary page
   ↓
7. Page is stored in TWO places:
   ✅ EditorService.editorState.page (Flash Persistence)
   ✅ PagesStateService.pages[] (Flash Persistence)
   ↓
8. Widgets stored in:
   ✅ EditorService.editorState.widgetsByPage[pageId] (Flash Persistence)

📦 Where Pages Are Persisted

1. EditorService Flash State (autoFlashState('editor-state'))

Storage Key: editor-state
Location: GgStorage (IndexedDB → Online Sync)

What's Stored:

{
  page: Page | null,  // Current active page metadata
  widgetsByPage: {    // Widgets organized by page ID
    [pageId: string]: Widget[]
  },
  // ... other editor state
}

Persistence: ✅ Automatic via Flash Persistence
Reload: ✅ Yes, persists across sessions


2. PagesStateService Flash State (autoFlashState('pages-state'))

Storage Key: pages-state
Location: GgStorage (IndexedDB → Online Sync)

What's Stored:

{
  pages: Page[],           // All pages (metadata only)
  activePage: Page | null, // Currently active page
  draftPages: Page[],      // Draft pages
  publishedPages: Page[],  // Published pages
  archivedPages: Page[]   // Archived pages
}

Persistence: ✅ Automatic via Flash Persistence
Reload: ✅ Yes, persists across sessions
Purpose: Page discovery, page selector, page management


🔄 The Complete Persistence Chain

When Creating a New Page:

// 1. User adds widget → EditorService.setWidgets()
if (!currentPageId && widgets.length > 0) {
  // 2. Create temporary page
  const tempPage: Page = {
    id: `page-${Date.now()}-${random}`,
    title: 'New Page',
    widgets: [],
    // ... full page structure
  };
  
  // 3. Set in EditorService (stores page metadata)
  this.setPage(tempPage);
  
  // 4. Register in PagesStateService (makes it discoverable)
  this.pagesStateService.addPageIfNotExists(tempPage);
  
  // 5. Store widgets (page-scoped)
  widgetsByPage[tempPage.id] = widgets;
}

When Updating Widget Properties:

// 1. User changes widget name → onPropertyChange()
editorService.updateWidget(updatedWidget);

// 2. updateWidget() updates BOTH:
this.editorState.update(state => ({
  widgets: [...],                    // Legacy array
  widgetsByPage: {                   // Page-scoped storage
    [pageId]: [updatedWidget, ...]   // ✅ Updated here!
  }
}));

// 3. Flash Persistence automatically saves to GgStorage
// 4. Changes persist across reloads

🗄️ Storage Architecture

Dual Storage System:

Component Storage Key Purpose Contains
EditorService editor-state Editor UI state Active page, widgets by page, selection, undo/redo
PagesStateService pages-state Page registry All pages metadata, active page, page lists

Why Two Storages?

  1. EditorService = Editor-specific state (selection, clipboard, zoom, etc.)
  2. PagesStateService = Page registry (discoverable pages, page management)

Both use Flash Persistence → Automatic save/load, offline sync, online sync


🔍 How to Verify Persistence

Check EditorService State:

// In browser console:
const editorState = await storage.get('editor-state');
console.log('Current page:', editorState.page);
console.log('Widgets by page:', editorState.widgetsByPage);

Check PagesStateService State:

// In browser console:
const pagesState = await storage.get('pages-state');
console.log('All pages:', pagesState.pages);
console.log('Active page:', pagesState.activePage);

Check GgStorage:

// In browser console:
const storage = window.ggStorage; // If exposed
const keys = await storage.list();
console.log('All storage keys:', keys);

✅ What Was Fixed

Before (Broken):

After (Fixed):


📍 Route: /space/{space}/editor

When you're at this route:

  1. No pageId → Blank canvas mode

  2. First widget added → Temporary page created

  3. Page saved to:

    • editor-state (EditorService) → Page metadata + widgets
    • pages-state (PagesStateService) → Page registry
  4. On reload:

    • PagesStateService loads all pages from pages-state
    • EditorService loads editor state from editor-state
    • If page exists, widgets load automatically

🎯 Summary

Where is it persisting?

How does it work?

Why two storages?

Everything works now! ✅