Projects included: - maerchenzauber (NestJS backend + Expo mobile + SvelteKit web + Astro landing) - manacore (Expo mobile + SvelteKit web + Astro landing) - manadeck (NestJS backend + Expo mobile + SvelteKit web) - memoro (Expo mobile + SvelteKit web + Astro landing) This commit preserves the current state before monorepo restructuring. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| components | ||
| contexts | ||
| hooks | ||
| services | ||
| store | ||
| BACKEND_DOC.md | ||
| index.ts | ||
| README.md | ||
| SpaceCard.tsx | ||
Spaces Feature
This document provides comprehensive documentation for the Spaces feature in the Memoro app.
Overview
The Spaces feature allows users to organize their memos into logical groups or containers. Each space can contain multiple memos, and users can manage spaces (create, edit, delete) directly from the app interface.
Architecture
The Spaces feature is implemented across multiple components:
- Express Backend API - Serves as the source of truth for space management and authentication
- Memoro App Frontend (in
/memoro_app/features/spaces/) - Provides UI and client logic - Supabase Database - Stores space data, accessed directly by the Express Backend
┌─────────────────┐ ┌────────────────────┐
│ │ API Calls │ │
│ Memoro App │───────────────►│ Express Backend │
│ (Frontend) │ │ (Source of Truth)│
│ │◄───────────────│ │
└─────────────────┘ └─────────┬──────────┘
│
│ Direct Database
│ Operations
▼
┌───────────────────┐
│ │
│ Supabase Database │
│ │
└───────────────────┘
Database Schema
Spaces Table
CREATE TABLE IF NOT EXISTS public.spaces (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT DEFAULT '',
color TEXT DEFAULT '#4CAF50',
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
is_default BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
Memo Spaces Junction Table
CREATE TABLE IF NOT EXISTS public.memo_spaces (
memo_id UUID NOT NULL REFERENCES public.memos(id) ON DELETE CASCADE,
space_id UUID NOT NULL REFERENCES public.spaces(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ DEFAULT NOW(),
PRIMARY KEY (memo_id, space_id)
);
The schema includes foreign key constraints to ensure referential integrity between spaces, memos, and users.
Authentication
The application uses custom JWT tokens generated by the mana-core-middleware:
- The user authenticates using email/password through the middleware API
- The middleware generates a JWT token with a
subclaim containing the user ID - This token is used for both Supabase and Express backend authentication
- Row Level Security (RLS) policies use
auth.jwt() ->> 'sub'to extract the user ID
Component Details
1. Space Service (Client-Side)
The spaceService.ts provides the following methods:
getSpaces()- Retrieves all spaces for the current usergetSpace(id)- Gets a specific space by IDcreateSpace(data)- Creates a new spaceupdateSpace(id, data)- Updates an existing spacedeleteSpace(id)- Deletes a space
These methods make API calls to the Express backend for all operations, ensuring that data is always managed through the source of truth before being synchronized to the local database.
2. Express Backend API
The Express Backend API serves as the intermediary between the app and the database. It:
- Accepts requests from the app with space data
- Validates the requests using the JWT token
- Performs operations directly on the Supabase database
- Returns responses to the client
The Express Backend handles all CRUD operations for spaces, ensuring data consistency.
3. Express Backend Controllers
The Express backend provides endpoints for space management:
GET /api/spaces- List all spaces for a userGET /api/spaces/:id- Get a specific spacePOST /api/spaces- Create a new spacePUT /api/spaces/:id- Update a spaceDELETE /api/spaces/:id- Delete a space
4. UI Components
The UI implementation includes:
SpacesScreen.tsx- Main screen for displaying and managing spacesSpaceCard.tsx- Component for displaying individual spacesSpaceContext.tsx- React context for sharing space data across components
Data Flow
When a user creates a space:
- The app calls
spaceService.createSpace() - SpaceService makes an API call to the Express backend
- The Express backend validates the request
- The Express backend creates the space directly in the Supabase database
- The Express backend returns the space data to the app
- The UI updates to show the new space
Row Level Security (RLS)
The spaces table uses RLS to ensure users can only access their own spaces:
-- For retrieving spaces
CREATE POLICY "Users can view their own spaces"
ON public.spaces
FOR SELECT
USING ((auth.jwt() ->> 'sub')::uuid = user_id);
-- For creating spaces
CREATE POLICY "Users can create their own spaces"
ON public.spaces
FOR INSERT
WITH CHECK ((auth.jwt() ->> 'sub')::uuid = user_id);
-- For updating spaces
CREATE POLICY "Users can update their own spaces"
ON public.spaces
FOR UPDATE
USING ((auth.jwt() ->> 'sub')::uuid = user_id);
-- For deleting spaces
CREATE POLICY "Users can delete their own spaces"
ON public.spaces
FOR DELETE
USING ((auth.jwt() ->> 'sub')::uuid = user_id);
-- For edge function operations
CREATE POLICY "Service role can access all spaces"
ON public.spaces
USING (auth.role() = 'service_role');
Usage Example
// Create a space
const newSpace = await spaceService.createSpace({
name: "Work Notes",
description: "All my work-related memos",
color: "#2196F3",
appId: "550e8400-e29b-41d4-a716-446655440000"
});
// Update a space
await spaceService.updateSpace(spaceId, {
name: "Updated Work Notes",
description: "New description",
color: "#FF9800"
});
// Delete a space
await spaceService.deleteSpace(spaceId);
Setup and Deployment
To fully set up the Spaces feature:
- Deploy the database migration in
/memoro_app/supabase/migrations/20240705000000_spaces_table.sql - Ensure the Express backend is running with space endpoints enabled
- Configure the Express backend with proper Supabase credentials
- Set up the appropriate environment variables in the app
Environments and Configuration
The feature respects the following environment variables:
EXPO_PUBLIC_MIDDLEWARE_URL- URL for the Express backend APIEXPO_PUBLIC_MIDDLEWARE_APP_ID- Application ID for the Memoro appEXPO_PUBLIC_SUPABASE_URL- URL for the Supabase project (used by Express backend)EXPO_PUBLIC_SUPABASE_ANON_KEY- Anon key for Supabase (used by Express backend)
Future Enhancements
Potential enhancements to consider:
- Space sharing with other users
- Space templates or blueprints
- Advanced filtering of memos within spaces
- Space-specific settings and preferences
- Hierarchical spaces (sub-spaces)
Troubleshooting
Common issues:
- Authentication failures - Ensure the JWT token is properly formatted with the
subclaim - RLS policy issues - Verify RLS policies are using the JWT claim correctly
- API communication problems - Check for network issues between the app and Express backend
- Database access issues - Ensure Express backend has proper permissions to access Supabase