Skip to main content

Nounspace Architecture Documentation

Space and Tab Management System​

Overview​

Nounspace features two distinct architectural patterns for space management: Public Spaces and Private Spaces (Homebase). While they share a common presentation layer through the SpacePage component, they differ significantly in state management, data flow, and tab handling.

Core Components​

┌─────────────────────┐           ┌─────────────────────┐
│ Public Spaces │ │ Private Spaces │
│ (Server + Client) │ │ (Client) │
└─────────┬───────────┘ └──────────┬──────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ SpacePage │◄─────────►│ Zustand Store │
└─────────┬───────────┘ └─────────────────────┘
│
▼
┌─────────────────────┐
│ Space │
└─────────────────────┘

1. Public Spaces Architecture​

Design Philosophy​

Public spaces follow a server/client separation pattern with clear boundaries for data transmission. They implement a unified SpacePageData interface with type-specific extensions.

Key Components​

Data Flow​

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│ Page (Server) │────►│ Type Space (CL) │────►│ PublicSpace │
│ Loads Data │ │ Adds isEditable │ │ Renders UI │
└─────────────────┘ └─────────────────┘ └─────────────────┘
  1. Server Components (page.tsx)

    • Load space data without client-side functions
    • Return Omit<SpaceData, 'isEditable' | 'spacePageUrl'>
    • Handle 404 cases and initial data loading
  2. Type-Specific Components (ProfileSpace.tsx, etc.)

    • Add client-side isEditable logic based on space type
    • Add spacePageUrl function for navigation
    • Pass complete data to PublicSpace
  3. Common Component (PublicSpace.tsx)

    • Handles space registration if needed
    • Manages tab rendering and switching
    • Coordinates state with Zustand store for edits

Tab Management​

Public spaces store tabs as a simple string array within the space configuration:

interface SpaceConfig {
// ... other properties
tabNames?: string[]; // Array of tab names in order
}

Tab Creation Process​

  1. Server loads initial tab configuration
  2. Client adds new tab to config.tabNames
  3. Updates are persisted through saveConfig and commitConfig

Tab Navigation​

URLs follow predictable patterns based on space type:

  • Profile: /s/username/TabName
  • Token: /t/network/address/TabName
  • Proposal: /p/proposalId/TabName

2. Private Spaces (Homebase) Architecture​

Design Philosophy​

Private spaces use a client-side Zustand store with optimistic updates and synchronized local/remote state management.

Key Components​

Data Flow​

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│ Zustand Store │◄───►│ PrivateSpace │────►│ SpacePage │
│ State Source │ │ Loads/Updates │ │ Renders UI │
└─────────────────┘ └─────────────────┘ └─────────────────┘
  1. Store Structure

    • homebaseStore.ts: Core state and actions
    • homebaseTabsStore.ts: Tab-specific logic
    • currentSpace: Tracking and navigation
  2. Tab Management

    • Special handling for Feed tab
    • Complex state synchronization
    • Optimistic UI updates

Tab Management​

Private spaces maintain separate structures for tab configuration and ordering:

interface HomeBaseTabStoreState {
tabs: {
[tabName: string]: {
config?: SpaceConfig; // Local tab configuration
remoteConfig?: SpaceConfig; // Last known server configuration
};
};
tabOrdering: {
local: string[]; // Current local order (possibly edited)
remote: string[]; // Last known server order
};
}

Tab Creation Process​

  1. Create local tab configuration
  2. Update local tab ordering
  3. Commit both to database separately
  4. Manage optimistic updates and rollbacks

Tab Navigation​

Uses clean URLs with special handling for the Feed tab:

  • Feed: /homebase
  • Other tabs: /homebase/TabName

3. Storage System​

Public Spaces​

  1. Supabase Tables:

    • spaceRegistrations: Maps spaceId to owner information
    • Stores type-specific metadata (fid, contract address, etc.)
  2. Supabase Storage:

    • Path format: spaces/${spaceId}/tabs/${tabName}
    • Tab order: spaces/${spaceId}/tabOrder
    • JSON files with configuration data
  3. API Routes:

    • /api/space/registry: Space registration
    • /api/space/registry/${spaceId}/tabs: Tab management

Private Spaces​

  1. Supabase Storage:

    • Path format: private/${identityPublicKey}/tabs/${tabName}
    • Tab order: private/${identityPublicKey}/tabOrder
    • Feed configuration: private/${identityPublicKey}/homebase
    • Encrypted for privacy
  2. API Routes:

    • /api/space/homebase: Feed configuration
    • /api/space/homebase/tabs: Tab management

4. Architectural Differences​

FeaturePublic SpacesPrivate Spaces
State ManagementServer + ClientClient-only Zustand
Tab StorageSingle tabNames arraySeparate tabOrdering object
Default TabType-specific ("Profile", "Token", etc.)Always "Feed"
Data FlowUnidirectionalBidirectional with store
Type SystemStrong interface inheritanceLoosely coupled store slices
InitializationServer-side data loadingClient-side data fetching
EditabilityType-specific rulesAlways editable by owner
URL StructureType-specific patternsHomebase-specific pattern

5. Common Infrastructure​

Both architectures share common components:

  1. SpacePage: Renders spaces with consistent UI
  2. TabBar: Renders and manages tab interactions
  3. Space: Core layout and fidget rendering
  4. Authentication: Common authentication system

6. Development Guidelines​

Working with Public Spaces​

  1. Add a New Public Space Type:

    src/app/(spaces)/{type}/[param]/
    ├── page.tsx # Server-side data loading
    ├── utils.ts # Helper functions
    ├── {Type}Space.tsx # Client-side component
    └── layout.tsx # Layout component
  2. Extending Space Data:

    interface NewTypeSpaceData extends SpacePageData {
    spaceType: typeof SPACE_TYPES.NEW_TYPE;
    defaultTab: 'DefaultTabName';
    // Type-specific properties
    }

Working with Private Spaces​

  1. Modify Tab Behavior:

    • Edit homebaseTabsStore.ts for tab management logic
    • Update PrivateSpace.tsx for tab rendering
  2. Add New Tab Actions:

    • Add action to store
    • Update UI components to trigger action
    • Handle optimistic updates and error states

7. Future Architectural Considerations​

For future development, consider:

  1. Unified Space Data Model:

    • Create a HomebaseSpaceData interface extending SpacePageData
    • Adapt PrivateSpace to construct this model from store
  2. Common Tab Management:

    • Move tab ordering to a consistent location across space types
    • Simplify TabBar to work with a unified tab interface
  3. Improved Type Safety:

    • Add stronger typing to Zustand store slices
    • Create type guards for private spaces similar to public spaces

This architecture provides flexibility for different space types while maintaining consistent user experience through shared rendering components.