mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 08:37:42 +02:00
style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
This commit is contained in:
parent
0241f5554c
commit
d36b321d9d
3952 changed files with 661498 additions and 739751 deletions
|
|
@ -85,11 +85,13 @@ claude mcp remove supabase-maerchenzauber
|
|||
Erstelle oder bearbeite die MCP-Konfigurationsdatei:
|
||||
|
||||
**Für lokales Scope** (nur für dich in diesem Projekt):
|
||||
|
||||
```bash
|
||||
# .mcp.json im Projekt-Root erstellen
|
||||
```
|
||||
|
||||
**Für User Scope** (global für alle deine Projekte):
|
||||
|
||||
```bash
|
||||
# ~/.config/claude/mcp.json
|
||||
```
|
||||
|
|
@ -98,12 +100,12 @@ Erstelle oder bearbeite die MCP-Konfigurationsdatei:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -179,12 +181,12 @@ Du kannst auf Datenbank-Ressourcen mit `@` verweisen:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-dev": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?project_ref=dyywxrmonxoiojsjmymc&features=database,development"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-dev": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?project_ref=dyywxrmonxoiojsjmymc&features=database,development"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -192,12 +194,12 @@ Du kannst auf Datenbank-Ressourcen mit `@` verweisen:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-prod": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-prod": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -205,12 +207,12 @@ Du kannst auf Datenbank-Ressourcen mit `@` verweisen:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-debug": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database,debugging"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-debug": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database,debugging"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -276,12 +278,12 @@ Du kannst Umgebungsvariablen in der MCP-Konfiguration nutzen:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=${SUPABASE_PROJECT_REF}"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=${SUPABASE_PROJECT_REF}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -291,16 +293,16 @@ Du kannst mehrere Supabase-Projekte gleichzeitig konfigurieren:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber-prod": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc"
|
||||
},
|
||||
"supabase-maerchenzauber-dev": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?project_ref=<dev-ref>"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber-prod": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc"
|
||||
},
|
||||
"supabase-maerchenzauber-dev": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?project_ref=<dev-ref>"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -310,17 +312,18 @@ Für Team-weite Konfiguration, erstelle `.mcp.json` im Projekt-Root:
|
|||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database",
|
||||
"description": "Märchenzauber Production DB (Read-Only)"
|
||||
}
|
||||
}
|
||||
"mcpServers": {
|
||||
"supabase-maerchenzauber": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.supabase.com/mcp?read_only=true&project_ref=dyywxrmonxoiojsjmymc&features=database",
|
||||
"description": "Märchenzauber Production DB (Read-Only)"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- Alle Teammitglieder nutzen die gleiche Konfiguration
|
||||
- Versionskontrolle über Git
|
||||
- Einheitliche Development-Experience
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,4 +7,4 @@ https://replicate.com/black-forest-labs/flux-1.1-pro
|
|||
https://replicate.com/stability-ai/stable-diffusion-3.5-large-turbo
|
||||
https://replicate.com/google/imagen-4
|
||||
https://replicate.com/stability-ai/stable-diffusion-3.5-medium
|
||||
https://replicate.com/google/nano-banana
|
||||
https://replicate.com/google/nano-banana
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
|
|||
```
|
||||
|
||||
This UUID is used consistently across:
|
||||
|
||||
- Database records (`user_id` field)
|
||||
- Backend validation logic
|
||||
- Frontend UI conditional rendering
|
||||
|
|
@ -62,6 +63,7 @@ INSERT INTO public.characters (
|
|||
The database uses RLS policies to allow all authenticated users to read system characters while protecting them from modification:
|
||||
|
||||
**Read Access Policy:**
|
||||
|
||||
```sql
|
||||
CREATE POLICY "Users can view their own characters and system characters"
|
||||
ON public.characters
|
||||
|
|
@ -92,6 +94,7 @@ if (!isSystemCharacter && !isOwnCharacter) {
|
|||
```
|
||||
|
||||
This pattern is applied in:
|
||||
|
||||
- `getCharacterById()` - GET /character/:id
|
||||
- `updateCharacter()` - PUT /character/:id
|
||||
- `deleteCharacter()` - DELETE /character/:id
|
||||
|
|
@ -145,6 +148,7 @@ path: 00000000-0000-0000-0000-000000000000/characters/[filename]
|
|||
```
|
||||
|
||||
Example URL:
|
||||
|
||||
```
|
||||
https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/00000000-0000-0000-0000-000000000000/characters/1762453771144-cmngxj-large.webp
|
||||
```
|
||||
|
|
@ -201,9 +205,9 @@ Use Supabase MCP tools to apply the migration to both development and production
|
|||
```typescript
|
||||
// Example using MCP tools
|
||||
await mcp__supabase__apply_migration({
|
||||
project_id: "your-project-id",
|
||||
name: "add_[character_name]_system_character",
|
||||
query: "INSERT INTO ..."
|
||||
project_id: 'your-project-id',
|
||||
name: 'add_[character_name]_system_character',
|
||||
query: 'INSERT INTO ...',
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -218,6 +222,7 @@ await mcp__supabase__apply_migration({
|
|||
- **Languages**: German (user_description) and English (character_description)
|
||||
|
||||
**Image Location**:
|
||||
|
||||
```
|
||||
https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/00000000-0000-0000-0000-000000000000/characters/1762453771144-cmngxj-large.webp
|
||||
```
|
||||
|
|
@ -225,23 +230,27 @@ https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/0
|
|||
## Best Practices
|
||||
|
||||
### Character Selection
|
||||
|
||||
- Choose characters with broad appeal for diverse story types
|
||||
- Ensure high-quality, consistent character images
|
||||
- Provide both German and English descriptions
|
||||
- Make characters suitable for children's stories
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
- Use descriptive, memorable names
|
||||
- Consider cultural appropriateness
|
||||
- Ensure uniqueness from user-created characters
|
||||
|
||||
### Image Requirements
|
||||
|
||||
- High resolution (recommended: 1024x1024 or higher)
|
||||
- Consistent art style across system characters
|
||||
- Child-appropriate content
|
||||
- Clear, recognizable character features
|
||||
|
||||
### Database Management
|
||||
|
||||
- Always use migrations for changes
|
||||
- Test in development before production
|
||||
- Include rollback procedures in migrations
|
||||
|
|
@ -252,6 +261,7 @@ https://dyywxrmonxoiojsjmymc.supabase.co/storage/v1/object/public/user-uploads/0
|
|||
### Character Not Visible to Users
|
||||
|
||||
**Check RLS Policies:**
|
||||
|
||||
```sql
|
||||
-- Verify the SELECT policy exists
|
||||
SELECT * FROM pg_policies
|
||||
|
|
@ -260,6 +270,7 @@ WHERE tablename = 'characters'
|
|||
```
|
||||
|
||||
**Verify User ID:**
|
||||
|
||||
```sql
|
||||
-- Ensure character has correct system user_id
|
||||
SELECT id, name, user_id
|
||||
|
|
@ -282,6 +293,7 @@ if (!isSystemCharacter && !isOwnCharacter) {
|
|||
```
|
||||
|
||||
Check these files:
|
||||
|
||||
- `backend/src/character/character.controller.ts` (GET, PUT, DELETE)
|
||||
- `backend/src/story/services/story-creation.service.ts` (character validation)
|
||||
|
||||
|
|
@ -299,18 +311,22 @@ Ensure the character object includes the `user_id` field from the API response.
|
|||
## Related Files
|
||||
|
||||
### Backend
|
||||
|
||||
- `backend/src/character/character.controller.ts` - Character CRUD endpoints
|
||||
- `backend/src/story/services/story-creation.service.ts` - Story creation validation
|
||||
- `backend/src/core/services/supabase-data.service.ts` - Database operations
|
||||
|
||||
### Frontend
|
||||
|
||||
- `mobile/app/character/[id].tsx` - Character detail screen
|
||||
- `mobile/src/utils/dataService.ts` - Character data service (removed hardcoded logic)
|
||||
|
||||
### Database
|
||||
|
||||
- Migration: `add_finnia_system_character_fixed`
|
||||
- Migration: `cleanup_conflicting_character_policies`
|
||||
|
||||
### Documentation
|
||||
|
||||
- `CLAUDE.md` - Main project documentation
|
||||
- This file - System characters documentation
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
# Märchenzauber - ToDos & Dokumentation
|
||||
# M<EFBFBD>rchenzauber - ToDos & Dokumentation
|
||||
|
||||
## <¨ Bildmodell-Auswahl Feature (10.09.2025)
|
||||
## <<EFBFBD> Bildmodell-Auswahl Feature (10.09.2025)
|
||||
|
||||
### Was wurde implementiert
|
||||
|
||||
#### Backend-Änderungen
|
||||
#### Backend-<EFBFBD>nderungen
|
||||
|
||||
1. **Datenbank-Schema**
|
||||
- Neue Tabelle `user_settings` mit Spalte `image_model` erstellt
|
||||
- RLS-Policies für sichere User-spezifische Einstellungen implementiert
|
||||
- RLS-Policies f<EFBFBD>r sichere User-spezifische Einstellungen implementiert
|
||||
- Migration-Script: `apps/backend/migrations/001_create_user_settings_table.sql`
|
||||
|
||||
2. **Model-Konfiguration** (`apps/backend/src/core/models/image-models.ts`)
|
||||
- Drei Bildgenerierungsmodelle definiert:
|
||||
- **FLUX Schnell**: Schnelle Generierung (1-2 Sek, 1 Credit)
|
||||
- **FLUX Pro 1.1**: Premium-Qualität (10-15 Sek, 3 Credits)
|
||||
- **Stable Diffusion XL**: Standard-Qualität (5-10 Sek, 2 Credits)
|
||||
- Vollständige Replicate Model-IDs mit Versionen integriert
|
||||
- **FLUX Pro 1.1**: Premium-Qualit<EFBFBD>t (10-15 Sek, 3 Credits)
|
||||
- **Stable Diffusion XL**: Standard-Qualit<EFBFBD>t (5-10 Sek, 2 Credits)
|
||||
- Vollst<EFBFBD>ndige Replicate Model-IDs mit Versionen integriert
|
||||
|
||||
3. **Settings Service** (`apps/backend/src/core/services/settings.service.ts`)
|
||||
- `getUserImageModel(userId)`: Abrufen des User-spezifischen Modells
|
||||
- `setUserImageModel(userId, modelId)`: Setzen eines neuen Modells
|
||||
- `getAvailableImageModels()`: Liste aller verfügbaren Modelle
|
||||
- `getAvailableImageModels()`: Liste aller verf<EFBFBD>gbaren Modelle
|
||||
- `getImageModelInfo(modelId)`: Detailinformationen zu einem Modell
|
||||
- Caching der User-Einstellungen für Performance
|
||||
- Caching der User-Einstellungen f<EFBFBD>r Performance
|
||||
|
||||
4. **Image Service Updates** (`apps/backend/src/core/services/image-supabase.service.ts`)
|
||||
- `generateImageWithReplicate()` nutzt jetzt User-spezifisches Modell
|
||||
|
|
@ -35,22 +35,22 @@
|
|||
- Drei Varianten aktualisiert: createCharacter, createCharacterFromImage, createCharacterSpecial
|
||||
|
||||
6. **Story Service Updates** (`apps/backend/src/story/services/story-creation.service.ts`)
|
||||
- `generateIllustrationForPage()` nutzt User-ID für Modellauswahl
|
||||
- Konsistente Modellnutzung für alle Illustrationen einer Story
|
||||
- `generateIllustrationForPage()` nutzt User-ID f<EFBFBD>r Modellauswahl
|
||||
- Konsistente Modellnutzung f<EFBFBD>r alle Illustrationen einer Story
|
||||
|
||||
7. **API Endpoints** (`apps/backend/src/settings/settings.controller.ts`)
|
||||
- `GET /settings/image-models`: Verfügbare Modelle mit Metadaten
|
||||
- `GET /settings/image-models`: Verf<EFBFBD>gbare Modelle mit Metadaten
|
||||
- `GET /settings/user/image-model`: Aktuelles User-Modell abrufen
|
||||
- `PUT /settings/user/image-model`: User-Modell ändern
|
||||
- DTO für Modell-Updates: `apps/backend/src/settings/dto/image-model.dto.ts`
|
||||
- `PUT /settings/user/image-model`: User-Modell <EFBFBD>ndern
|
||||
- DTO f<EFBFBD>r Modell-Updates: `apps/backend/src/settings/dto/image-model.dto.ts`
|
||||
|
||||
#### Frontend-Änderungen
|
||||
#### Frontend-<EFBFBD>nderungen
|
||||
|
||||
1. **Neue Settings-Seite** (`apps/mobile/app/(tabs)/(settings)/image-model.tsx`)
|
||||
- Übersichtliche Card-basierte Modellauswahl
|
||||
- Visual Feedback für ausgewähltes Modell
|
||||
- Badges für Geschwindigkeit und Credit-Kosten
|
||||
- Informative Beschreibungen für jedes Modell
|
||||
- <EFBFBD>bersichtliche Card-basierte Modellauswahl
|
||||
- Visual Feedback f<EFBFBD>r ausgew<65>hltes Modell
|
||||
- Badges f<EFBFBD>r Geschwindigkeit und Credit-Kosten
|
||||
- Informative Beschreibungen f<EFBFBD>r jedes Modell
|
||||
- Loading- und Saving-States
|
||||
- Error Handling mit User-Feedback
|
||||
|
||||
|
|
@ -59,16 +59,17 @@
|
|||
- Navigation zur Modellauswahl-Seite
|
||||
|
||||
3. **UI Features**
|
||||
- Responsive Design für verschiedene Bildschirmgrößen
|
||||
- Responsive Design f<EFBFBD>r verschiedene Bildschirmgr<67><72>en
|
||||
- Farbcodierte Badges (Schnell/Premium/Standard)
|
||||
- Checkmark-Icon für ausgewähltes Modell
|
||||
- Info-Box mit Erklärung zur Modellauswahl
|
||||
- Checkmark-Icon f<EFBFBD>r ausgew<65>hltes Modell
|
||||
- Info-Box mit Erkl<EFBFBD>rung zur Modellauswahl
|
||||
|
||||
### =' Was muss noch gemacht werden
|
||||
|
||||
#### Sofort erforderlich (vor Go-Live)
|
||||
|
||||
1. **Datenbank-Migration ausführen** KRITISCH
|
||||
1. **Datenbank-Migration ausf<73>hren** <20> KRITISCH
|
||||
|
||||
```bash
|
||||
# In Supabase Dashboard oder via psql:
|
||||
psql "postgresql://postgres.[project-id]:[password]@db.dyywxrmonxoiojsjmymc.supabase.co:5432/postgres"
|
||||
|
|
@ -76,49 +77,50 @@
|
|||
```
|
||||
|
||||
2. **Backend neu starten**
|
||||
|
||||
```bash
|
||||
cd apps/backend
|
||||
npm run dev # oder npm run start:prod für Produktion
|
||||
npm run dev # oder npm run start:prod f<EFBFBD>r Produktion
|
||||
```
|
||||
|
||||
3. **Testing**
|
||||
- [ ] Modellauswahl in der App testen
|
||||
- [ ] Story-Generierung mit verschiedenen Modellen testen
|
||||
- [ ] Character-Erstellung mit verschiedenen Modellen testen
|
||||
- [ ] Persistenz der Einstellungen prüfen
|
||||
- [ ] Persistenz der Einstellungen pr<EFBFBD>fen
|
||||
|
||||
#### Nächste Schritte (Nice-to-have)
|
||||
#### N<EFBFBD>chste Schritte (Nice-to-have)
|
||||
|
||||
1. **Performance & Monitoring**
|
||||
- [ ] Logging für Modell-Performance (Generierungszeiten)
|
||||
- [ ] Logging f<EFBFBD>r Modell-Performance (Generierungszeiten)
|
||||
- [ ] Erfolgs-/Fehlerquoten pro Modell tracken
|
||||
- [ ] Analytics für Modell-Präferenzen der User
|
||||
- [ ] Analytics f<EFBFBD>r Modell-Pr<50>ferenzen der User
|
||||
|
||||
2. **User Experience**
|
||||
- [ ] Modell-Empfehlungen basierend auf Use-Case
|
||||
- [ ] Vorschau-Bilder für jedes Modell
|
||||
- [ ] A/B Testing für optimale Default-Einstellung
|
||||
- [ ] Vorschau-Bilder f<EFBFBD>r jedes Modell
|
||||
- [ ] A/B Testing f<EFBFBD>r optimale Default-Einstellung
|
||||
|
||||
3. **Admin Features**
|
||||
- [ ] Admin-Dashboard für Modell-Statistiken
|
||||
- [ ] Admin-Dashboard f<EFBFBD>r Modell-Statistiken
|
||||
- [ ] Modelle dynamisch aktivieren/deaktivieren
|
||||
- [ ] Credit-Preise anpassen per Admin-Interface
|
||||
|
||||
4. **Erweiterte Features**
|
||||
- [ ] Verschiedene Modelle für Characters vs. Stories
|
||||
- [ ] User-Feedback zur Bildqualität erfassen
|
||||
- [ ] Verschiedene Modelle f<EFBFBD>r Characters vs. Stories
|
||||
- [ ] User-Feedback zur Bildqualit<EFBFBD>t erfassen
|
||||
- [ ] Automatisches Fallback bei Modell-Ausfall
|
||||
- [ ] Batch-Generierung mit mehreren Modellen (für Vergleich)
|
||||
- [ ] Batch-Generierung mit mehreren Modellen (f<EFBFBD>r Vergleich)
|
||||
|
||||
### =Ê Technische Details
|
||||
### =<EFBFBD> Technische Details
|
||||
|
||||
#### Modell-Spezifikationen
|
||||
|
||||
| Modell | Replicate ID | Geschwindigkeit | Credits | Use-Case |
|
||||
|--------|-------------|-----------------|---------|----------|
|
||||
| FLUX Schnell | `black-forest-labs/flux-schnell:5599ed30...` | 1-2s | 1 | Tests, Prototypen |
|
||||
| FLUX Pro 1.1 | `black-forest-labs/flux-1.1-pro:8f06b9d3...` | 10-15s | 3 | Finale Stories |
|
||||
| SDXL | `stability-ai/sdxl:39ed52f2...` | 5-10s | 2 | Standard |
|
||||
| Modell | Replicate ID | Geschwindigkeit | Credits | Use-Case |
|
||||
| ------------ | -------------------------------------------- | --------------- | ------- | ----------------- |
|
||||
| FLUX Schnell | `black-forest-labs/flux-schnell:5599ed30...` | 1-2s | 1 | Tests, Prototypen |
|
||||
| FLUX Pro 1.1 | `black-forest-labs/flux-1.1-pro:8f06b9d3...` | 10-15s | 3 | Finale Stories |
|
||||
| SDXL | `stability-ai/sdxl:39ed52f2...` | 5-10s | 2 | Standard |
|
||||
|
||||
#### Datenbank-Schema
|
||||
|
||||
|
|
@ -134,22 +136,23 @@ CREATE TABLE user_settings (
|
|||
|
||||
### = Bekannte Issues & Fixes
|
||||
|
||||
1. **Import-Pfad-Fehler (GELÖST)**
|
||||
1. **Import-Pfad-Fehler (GEL<EFBFBD>ST)**
|
||||
- Problem: `Colors` Import-Pfad war falsch
|
||||
- Lösung: Pfad von `../../../src/constants/Colors` zu `../../../constants/Colors` korrigiert
|
||||
- L<EFBFBD>sung: Pfad von `../../../src/constants/Colors` zu `../../../constants/Colors` korrigiert
|
||||
|
||||
2. **TypeScript Strict Mode**
|
||||
- Linter hat `!` zu required properties hinzugefügt
|
||||
- Linter hat `!` zu required properties hinzugef<EFBFBD>gt
|
||||
- DTOs nutzen jetzt strict mode compliance
|
||||
|
||||
### =Ý Testing Checklist
|
||||
### =<EFBFBD> Testing Checklist
|
||||
|
||||
- [ ] **Backend API Tests**
|
||||
|
||||
```bash
|
||||
# Modelle abrufen
|
||||
curl http://localhost:3002/settings/image-models -H "Authorization: Bearer TOKEN"
|
||||
|
||||
# User-Modell ändern
|
||||
|
||||
# User-Modell <EFBFBD>ndern
|
||||
curl -X PUT http://localhost:3002/settings/user/image-model \
|
||||
-H "Authorization: Bearer TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
|
|
@ -157,55 +160,59 @@ CREATE TABLE user_settings (
|
|||
```
|
||||
|
||||
- [ ] **Frontend Tests**
|
||||
- Settings öffnen ’ Bildgenerierung
|
||||
- Modell auswählen und speichern
|
||||
- App neu starten und prüfen ob Auswahl persistiert
|
||||
- Settings <EFBFBD>ffnen <20> Bildgenerierung
|
||||
- Modell ausw<EFBFBD>hlen und speichern
|
||||
- App neu starten und pr<EFBFBD>fen ob Auswahl persistiert
|
||||
- Neue Story/Character erstellen
|
||||
|
||||
- [ ] **Datenbank Tests**
|
||||
|
||||
```sql
|
||||
-- User-Settings prüfen
|
||||
-- User-Settings pr<EFBFBD>fen
|
||||
SELECT * FROM user_settings;
|
||||
|
||||
|
||||
-- Modell-Verteilung
|
||||
SELECT image_model, COUNT(*) FROM user_settings GROUP BY image_model;
|
||||
```
|
||||
|
||||
### =Ú Dokumentation
|
||||
### =<EFBFBD> Dokumentation
|
||||
|
||||
- Vollständige Implementation-Dokumentation: `/IMAGE_MODEL_SELECTION_README.md`
|
||||
- Vollst<EFBFBD>ndige Implementation-Dokumentation: `/IMAGE_MODEL_SELECTION_README.md`
|
||||
- API-Dokumentation in den jeweiligen Controller-Dateien
|
||||
- Frontend-Komponenten sind self-documenting mit TypeScript-Interfaces
|
||||
|
||||
### =€ Deployment Checklist
|
||||
### =<EFBFBD> Deployment Checklist
|
||||
|
||||
1. [ ] Datenbank-Migration ausführen
|
||||
2. [ ] Environment-Variable `MAERCHENZAUBER_REPLICATE_API_KEY` prüfen
|
||||
1. [ ] Datenbank-Migration ausf<EFBFBD>hren
|
||||
2. [ ] Environment-Variable `MAERCHENZAUBER_REPLICATE_API_KEY` pr<EFBFBD>fen
|
||||
3. [ ] Backend deployen
|
||||
4. [ ] Mobile App builden und deployen
|
||||
5. [ ] Feature-Flag aktivieren (falls vorhanden)
|
||||
6. [ ] Monitoring aktivieren
|
||||
7. [ ] User-Kommunikation über neues Feature
|
||||
7. [ ] User-Kommunikation <EFBFBD>ber neues Feature
|
||||
|
||||
---
|
||||
|
||||
## =Å Weitere TODOs (nicht related zu Bildmodell-Feature)
|
||||
## =<EFBFBD> Weitere TODOs (nicht related zu Bildmodell-Feature)
|
||||
|
||||
### High Priority
|
||||
- [ ] Story-Logbook vollständig implementieren
|
||||
|
||||
- [ ] Story-Logbook vollst<73>ndig implementieren
|
||||
- [ ] Character-Konsistenz in Stories verbessern
|
||||
- [ ] 10 Bilder pro Story generieren (aktuell nur 3)
|
||||
|
||||
### Medium Priority
|
||||
- [ ] Mana Core Credit-System Integration vervollständigen
|
||||
|
||||
- [ ] Mana Core Credit-System Integration vervollst<73>ndigen
|
||||
- [ ] Mehrsprachigkeit (DE/EN) konsistent umsetzen
|
||||
- [ ] Performance-Optimierungen für große Story-Collections
|
||||
- [ ] Performance-Optimierungen f<EFBFBD>r gro<72>e Story-Collections
|
||||
|
||||
### Low Priority
|
||||
- [ ] Onboarding-Flow überarbeiten
|
||||
|
||||
- [ ] Onboarding-Flow <20>berarbeiten
|
||||
- [ ] Push-Notifications implementieren
|
||||
- [ ] Social-Sharing Features
|
||||
|
||||
---
|
||||
|
||||
*Letzte Aktualisierung: 10.09.2025 - Till Schneider*
|
||||
_Letzte Aktualisierung: 10.09.2025 - Till Schneider_
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
Märchenzauber ist eine gut strukturierte, moderne App mit starkem technologischen Fundament (NestJS, React Native, Expo, Supabase). Die Analyse hat **62 konkrete Verbesserungsmöglichkeiten** identifiziert, die in folgende Kategorien fallen:
|
||||
|
||||
### Stärken der aktuellen Implementierung
|
||||
|
||||
- ✅ Saubere Modulstruktur (Backend + Frontend)
|
||||
- ✅ Moderne Tech-Stack mit TypeScript
|
||||
- ✅ Gute Komponenten-Architektur (Atomic Design)
|
||||
|
|
@ -33,6 +34,7 @@ Märchenzauber ist eine gut strukturierte, moderne App mit starkem technologisch
|
|||
- ✅ Responsive Design für verschiedene Bildschirmgrößen
|
||||
|
||||
### Hauptverbesserungspotenziale
|
||||
|
||||
- 🔴 **Kritisch:** Fehlende Fehlerbehandlung in mobilen Komponenten
|
||||
- 🟡 **Wichtig:** Performance-Optimierungen (Caching, Lazy Loading)
|
||||
- 🟢 **Nice-to-Have:** UX-Verbesserungen (Animationen, Feedback)
|
||||
|
|
@ -46,6 +48,7 @@ Märchenzauber ist eine gut strukturierte, moderne App mit starkem technologisch
|
|||
**Problem:** Die App verwendet nur Context API + lokale useState. Bei wachsender Komplexität wird dies zu Prop-Drilling und Re-Render-Problemen führen.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// Empfehlung: Zustand für globalen State
|
||||
import { create } from 'zustand';
|
||||
|
|
@ -76,6 +79,7 @@ export const useStoryStore = create<StoryStore>()(
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- 🚀 Weniger Re-Renders durch selektive Subscriptions
|
||||
- 💾 Automatisches Persistence
|
||||
- 🧪 Bessere Testbarkeit
|
||||
|
|
@ -90,6 +94,7 @@ export const useStoryStore = create<StoryStore>()(
|
|||
**Problem:** Turborepo ist konfiguriert, aber `package.json` Scripts nutzen es nicht optimal.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```json
|
||||
// Root package.json
|
||||
{
|
||||
|
|
@ -120,6 +125,7 @@ export const useStoryStore = create<StoryStore>()(
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- ⚡ Parallele Task-Ausführung
|
||||
- 💾 Build-Caching zwischen Runs
|
||||
- 🔄 Automatische Dependency-Resolution
|
||||
|
|
@ -133,6 +139,7 @@ export const useStoryStore = create<StoryStore>()(
|
|||
**Problem:** Types werden zwischen Mobile und Backend dupliziert.
|
||||
|
||||
**Aktuelle Situation:**
|
||||
|
||||
```typescript
|
||||
// mobile/types/character.ts
|
||||
export interface Character {
|
||||
|
|
@ -152,6 +159,7 @@ export interface Character {
|
|||
```
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// packages/shared-types/src/character.ts
|
||||
export interface Character {
|
||||
|
|
@ -179,6 +187,7 @@ export class CharacterDto {
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- ✅ Single Source of Truth
|
||||
- 🔒 Type-Safety zwischen Services
|
||||
- 🔄 Automatische API-Validierung
|
||||
|
|
@ -194,6 +203,7 @@ export class CharacterDto {
|
|||
**Problem:** `expo-image` wird verwendet, aber keine explizite Cache-Policy definiert.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// components/atoms/OptimizedImage.tsx
|
||||
import { Image, ImageContentFit } from 'expo-image';
|
||||
|
|
@ -228,12 +238,13 @@ export function OptimizedImage({ uri, blurHash, size, aspectRatio = 16/9 }: Prop
|
|||
```
|
||||
|
||||
**Zusätzlich: Preloading für Story Pages**
|
||||
|
||||
```typescript
|
||||
// In StoryViewer beim Page Change
|
||||
useEffect(() => {
|
||||
// Preload next 2 pages
|
||||
const nextPages = [currentPage + 1, currentPage + 2];
|
||||
nextPages.forEach(pageNum => {
|
||||
nextPages.forEach((pageNum) => {
|
||||
const page = pages[pageNum];
|
||||
if (page?.image_url) {
|
||||
Image.prefetch(page.image_url);
|
||||
|
|
@ -243,6 +254,7 @@ useEffect(() => {
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- 🚀 Schnelleres Laden (50-70% Reduktion)
|
||||
- 💾 Reduzierter Daten-Traffic
|
||||
- 🎨 Smooth BlurHash Transitions
|
||||
|
|
@ -256,6 +268,7 @@ useEffect(() => {
|
|||
**Problem:** `FlatList` rendert alle Stories, auch wenn sie nicht sichtbar sind.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// app/stories.tsx
|
||||
import { FlashList } from '@shopify/flash-list';
|
||||
|
|
@ -296,6 +309,7 @@ export default function StoriesScreen() {
|
|||
**Problem:** Identische Prompts führen zu redundanten AI-Calls (teuer & langsam).
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/core/services/caching.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
|
@ -326,15 +340,14 @@ export class CachingService {
|
|||
}
|
||||
|
||||
private generateKey(prompt: string, model: string): string {
|
||||
const hash = createHash('sha256')
|
||||
.update(`${model}:${prompt}`)
|
||||
.digest('hex');
|
||||
const hash = createHash('sha256').update(`${model}:${prompt}`).digest('hex');
|
||||
return `ai-prompt:${hash}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration in PromptingService:**
|
||||
|
||||
```typescript
|
||||
async createConsistentCharacterDescriptionPrompts(
|
||||
story: string,
|
||||
|
|
@ -369,6 +382,7 @@ async createConsistentCharacterDescriptionPrompts(
|
|||
```
|
||||
|
||||
**Kostenersparnis (bei 1000 Stories/Monat):**
|
||||
|
||||
- Cache Hit Rate: ~30% (konservativ)
|
||||
- AI Calls vermieden: 300/Monat
|
||||
- Kosteneinsparung: **~$45-60/Monat**
|
||||
|
|
@ -383,6 +397,7 @@ async createConsistentCharacterDescriptionPrompts(
|
|||
**Problem:** User sieht nur "Lädt..." ohne Fortschrittsanzeige.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// Backend: WebSocket oder SSE für Progress Updates
|
||||
// Alternative: Polling-basiertes System mit story_logbooks
|
||||
|
|
@ -431,6 +446,7 @@ export default function CreateStory() {
|
|||
```
|
||||
|
||||
**Progress Steps:**
|
||||
|
||||
1. ✅ Charakter wird geladen (10%)
|
||||
2. 📝 Geschichte wird geschrieben (30%)
|
||||
3. 🎨 Illustrationen werden erstellt (70%)
|
||||
|
|
@ -448,6 +464,7 @@ export default function CreateStory() {
|
|||
**Problem:** Keine klare Guided Tour für neue User.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// components/organisms/OnboardingTour.tsx
|
||||
import { useEffect, useState } from 'react';
|
||||
|
|
@ -459,22 +476,22 @@ const ONBOARDING_STEPS = [
|
|||
title: 'Willkommen bei Märchenzauber!',
|
||||
description: 'Erstelle magische Geschichten mit deinen eigenen Charakteren',
|
||||
target: null,
|
||||
cta: 'Los gehts!'
|
||||
cta: 'Los gehts!',
|
||||
},
|
||||
{
|
||||
id: 'create-character',
|
||||
title: 'Erstelle deinen ersten Charakter',
|
||||
description: 'Lade ein Foto hoch oder beschreibe deinen Charakter',
|
||||
target: 'create-character-button',
|
||||
cta: 'Charakter erstellen'
|
||||
cta: 'Charakter erstellen',
|
||||
},
|
||||
{
|
||||
id: 'create-story',
|
||||
title: 'Schreibe deine erste Geschichte',
|
||||
description: 'Beschreibe was passieren soll - die KI macht den Rest!',
|
||||
target: 'create-story-button',
|
||||
cta: 'Geschichte schreiben'
|
||||
}
|
||||
cta: 'Geschichte schreiben',
|
||||
},
|
||||
];
|
||||
|
||||
export function OnboardingTour({ onComplete }: { onComplete: () => void }) {
|
||||
|
|
@ -485,7 +502,7 @@ export function OnboardingTour({ onComplete }: { onComplete: () => void }) {
|
|||
Animated.timing(fadeAnim, {
|
||||
toValue: 1,
|
||||
duration: 300,
|
||||
useNativeDriver: true
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
}, [currentStep]);
|
||||
|
||||
|
|
@ -494,11 +511,12 @@ export function OnboardingTour({ onComplete }: { onComplete: () => void }) {
|
|||
```
|
||||
|
||||
**Tracking:**
|
||||
|
||||
```typescript
|
||||
posthog?.capture('onboarding_step_viewed', {
|
||||
step: currentStep,
|
||||
step_name: ONBOARDING_STEPS[currentStep].id,
|
||||
time_spent: Date.now() - stepStartTime
|
||||
time_spent: Date.now() - stepStartTime,
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -511,6 +529,7 @@ posthog?.capture('onboarding_step_viewed', {
|
|||
**Problem:** Share-Funktion vorhanden, aber kein Share-Sheet mit Preview.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// components/molecules/ShareButton.tsx
|
||||
import * as Sharing from 'expo-sharing';
|
||||
|
|
@ -572,6 +591,7 @@ export function ShareButton({ story }: { story: Story }) {
|
|||
```
|
||||
|
||||
**Zusätzlich: Deep Links für Shares**
|
||||
|
||||
```typescript
|
||||
// app.json
|
||||
{
|
||||
|
|
@ -603,6 +623,7 @@ export function ShareButton({ story }: { story: Story }) {
|
|||
**Problem:** App funktioniert nicht ohne Internet, auch für bereits geladene Stories.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// src/services/offlineStorageService.ts
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
|
|
@ -612,16 +633,13 @@ export class OfflineStorageService {
|
|||
private static KEYS = {
|
||||
STORIES: 'offline_stories',
|
||||
CHARACTERS: 'offline_characters',
|
||||
IMAGES: 'offline_images'
|
||||
IMAGES: 'offline_images',
|
||||
};
|
||||
|
||||
static async saveStoryForOffline(story: Story): Promise<void> {
|
||||
const stories = await this.getOfflineStories();
|
||||
const updated = [...stories.filter(s => s.id !== story.id), story];
|
||||
await AsyncStorage.setItem(
|
||||
this.KEYS.STORIES,
|
||||
JSON.stringify(updated)
|
||||
);
|
||||
const updated = [...stories.filter((s) => s.id !== story.id), story];
|
||||
await AsyncStorage.setItem(this.KEYS.STORIES, JSON.stringify(updated));
|
||||
}
|
||||
|
||||
static async getOfflineStories(): Promise<Story[]> {
|
||||
|
|
@ -649,20 +667,19 @@ export const dataService = {
|
|||
const stories = await fetchWithAuth('/story');
|
||||
|
||||
// Cache for offline use
|
||||
await Promise.all(
|
||||
stories.map(story => OfflineStorageService.saveStoryForOffline(story))
|
||||
);
|
||||
await Promise.all(stories.map((story) => OfflineStorageService.saveStoryForOffline(story)));
|
||||
|
||||
return stories;
|
||||
} catch (error) {
|
||||
console.warn('Network error - falling back to offline cache');
|
||||
return await OfflineStorageService.getOfflineStories();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
**Offline Banner:**
|
||||
|
||||
```typescript
|
||||
// components/molecules/OfflineBanner.tsx
|
||||
export function OfflineBanner() {
|
||||
|
|
@ -699,6 +716,7 @@ export function OfflineBanner() {
|
|||
**Vorschläge:**
|
||||
|
||||
**A) Text-to-Speech für Geschichten**
|
||||
|
||||
```typescript
|
||||
import * as Speech from 'expo-speech';
|
||||
|
||||
|
|
@ -733,6 +751,7 @@ function StoryPage({ page }: { page: StoryPage }) {
|
|||
```
|
||||
|
||||
**B) Immersive Reading Mode**
|
||||
|
||||
```typescript
|
||||
// Automatisches Durchblättern mit Timer
|
||||
function StoryViewer({ pages }: { pages: StoryPage[] }) {
|
||||
|
|
@ -769,6 +788,7 @@ function StoryViewer({ pages }: { pages: StoryPage[] }) {
|
|||
```
|
||||
|
||||
**C) Bookmarking & Reading Progress**
|
||||
|
||||
```typescript
|
||||
interface ReadingProgress {
|
||||
storyId: string;
|
||||
|
|
@ -785,7 +805,7 @@ useEffect(() => {
|
|||
JSON.stringify({
|
||||
currentPage,
|
||||
lastReadAt: new Date().toISOString(),
|
||||
completed: currentPage >= pages.length - 1
|
||||
completed: currentPage >= pages.length - 1,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
@ -816,6 +836,7 @@ useEffect(() => {
|
|||
**Problem:** Character-Liste ist einfach, aber bietet keine Sortierung/Filterung.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// app/characters.tsx
|
||||
export default function CharactersScreen() {
|
||||
|
|
@ -866,6 +887,7 @@ export default function CharactersScreen() {
|
|||
```
|
||||
|
||||
**Zusätzlich: Character Stats**
|
||||
|
||||
```typescript
|
||||
// backend: Add story_count to character response
|
||||
async getUserCharacters(userId: string) {
|
||||
|
|
@ -895,6 +917,7 @@ async getUserCharacters(userId: string) {
|
|||
**Problem:** Responses haben unterschiedliche Formate (`Result<T>` vs. direkte Returns).
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// Standardisiertes API Response Format
|
||||
export interface ApiResponse<T = any> {
|
||||
|
|
@ -919,28 +942,28 @@ export class ResponseTransformInterceptor implements NestInterceptor {
|
|||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
return next.handle().pipe(
|
||||
map(data => ({
|
||||
map((data) => ({
|
||||
success: true,
|
||||
data,
|
||||
meta: {
|
||||
timestamp: new Date().toISOString(),
|
||||
requestId: request.id,
|
||||
version: '1.0'
|
||||
}
|
||||
version: '1.0',
|
||||
},
|
||||
})),
|
||||
catchError(error => {
|
||||
catchError((error) => {
|
||||
return of({
|
||||
success: false,
|
||||
error: {
|
||||
code: error.code || 'INTERNAL_ERROR',
|
||||
message: error.message,
|
||||
details: error.details
|
||||
details: error.details,
|
||||
},
|
||||
meta: {
|
||||
timestamp: new Date().toISOString(),
|
||||
requestId: request.id,
|
||||
version: '1.0'
|
||||
}
|
||||
version: '1.0',
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
@ -949,6 +972,7 @@ export class ResponseTransformInterceptor implements NestInterceptor {
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- ✅ Konsistente Responses
|
||||
- 🔍 Besseres Error-Tracking mit Request-IDs
|
||||
- 📊 Vereinfachte Client-Side Parsing
|
||||
|
|
@ -962,6 +986,7 @@ export class ResponseTransformInterceptor implements NestInterceptor {
|
|||
**Problem:** Keine Rate Limits auf teure AI-Endpoints.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/common/guards/rate-limit.guard.ts
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
|
|
@ -976,11 +1001,14 @@ export class CustomRateLimitGuard extends ThrottlerGuard {
|
|||
}
|
||||
|
||||
protected async throwThrottlingException(context: ExecutionContext): Promise<void> {
|
||||
throw new HttpException({
|
||||
statusCode: 429,
|
||||
message: 'Zu viele Anfragen. Bitte versuche es in ein paar Minuten erneut.',
|
||||
retryAfter: 60
|
||||
}, 429);
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: 429,
|
||||
message: 'Zu viele Anfragen. Bitte versuche es in ein paar Minuten erneut.',
|
||||
retryAfter: 60,
|
||||
},
|
||||
429
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1029,6 +1057,7 @@ export class StoryController {
|
|||
**Problem:** Health Checks existieren, aber prüfen nur Basis-Status.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/health/health.controller.ts
|
||||
import { HealthCheck, HealthCheckService, TypeOrmHealthIndicator } from '@nestjs/terminus';
|
||||
|
|
@ -1040,7 +1069,7 @@ export class HealthController {
|
|||
private db: TypeOrmHealthIndicator,
|
||||
private supabase: SupabaseHealthIndicator,
|
||||
private replicate: ReplicateHealthIndicator,
|
||||
private manaCore: ManaCoreHealthIndicator,
|
||||
private manaCore: ManaCoreHealthIndicator
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
|
|
@ -1091,6 +1120,7 @@ export class HealthController {
|
|||
```
|
||||
|
||||
**Monitoring Dashboard Integration:**
|
||||
|
||||
```typescript
|
||||
// Prometheus Metrics
|
||||
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
|
||||
|
|
@ -1116,9 +1146,7 @@ export class MetricsService {
|
|||
});
|
||||
|
||||
recordStoryCreation(duration: number, success: boolean) {
|
||||
this.storyCreationDuration
|
||||
.labels(success ? 'success' : 'error')
|
||||
.observe(duration);
|
||||
this.storyCreationDuration.labels(success ? 'success' : 'error').observe(duration);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1132,6 +1160,7 @@ export class MetricsService {
|
|||
**Problem:** Mobile App muss pollen, um zu wissen, wann Story fertig ist.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/webhooks/webhook.service.ts
|
||||
@Injectable()
|
||||
|
|
@ -1182,6 +1211,7 @@ async createStory(params: StoryCreationParams): Promise<StoryCreationResult> {
|
|||
```
|
||||
|
||||
**Mobile: Push Notification Handling**
|
||||
|
||||
```typescript
|
||||
// mobile/src/services/pushNotificationService.ts
|
||||
import * as Notifications from 'expo-notifications';
|
||||
|
|
@ -1221,7 +1251,7 @@ export class PushNotificationService {
|
|||
});
|
||||
|
||||
// Handle notification tap
|
||||
Notifications.addNotificationResponseReceivedListener(response => {
|
||||
Notifications.addNotificationResponseReceivedListener((response) => {
|
||||
const { storyId, type } = response.notification.request.content.data;
|
||||
|
||||
if (type === 'story_completed') {
|
||||
|
|
@ -1247,6 +1277,7 @@ export class PushNotificationService {
|
|||
**Problem:** `updateCharacterImagesJsonb()` und ähnliche Methoden werden manuell aufgerufen.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```sql
|
||||
-- backend/migrations/auto_sync_jsonb_fields.sql
|
||||
|
||||
|
|
@ -1312,6 +1343,7 @@ CREATE TRIGGER story_pages_sync
|
|||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- ✅ Automatische Konsistenz
|
||||
- 🚀 Schnellere Abfragen (JSONB für Reads)
|
||||
- 🔍 Relationale Integrität (für komplexe Queries)
|
||||
|
|
@ -1325,6 +1357,7 @@ CREATE TRIGGER story_pages_sync
|
|||
**Problem:** Search-Bar in `stories.tsx` nutzt nur `includes()` auf Titel/Prompt.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```sql
|
||||
-- Add Full-Text Search columns
|
||||
ALTER TABLE stories
|
||||
|
|
@ -1358,6 +1391,7 @@ UPDATE stories SET search_vector =
|
|||
```
|
||||
|
||||
**Backend API:**
|
||||
|
||||
```typescript
|
||||
// story.controller.ts
|
||||
@Get('search')
|
||||
|
|
@ -1385,6 +1419,7 @@ async searchStories(query: string, userId: string): Promise<Story[]> {
|
|||
```
|
||||
|
||||
**Mobile Integration:**
|
||||
|
||||
```typescript
|
||||
// app/stories.tsx
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
|
@ -1420,6 +1455,7 @@ return (
|
|||
**Problem:** Keine explizite Backup-Strategie dokumentiert.
|
||||
|
||||
**Empfehlung (Supabase-spezifisch):**
|
||||
|
||||
```bash
|
||||
# Tägliche Backups via Supabase CLI
|
||||
# .github/workflows/database-backup.yml
|
||||
|
|
@ -1455,6 +1491,7 @@ jobs:
|
|||
```
|
||||
|
||||
**Point-in-Time Recovery:**
|
||||
|
||||
```sql
|
||||
-- Supabase bietet 7-Tage PITR (Pro Plan)
|
||||
-- Restore zu spezifischem Zeitpunkt:
|
||||
|
|
@ -1472,6 +1509,7 @@ jobs:
|
|||
**Problem:** Keine End-to-End Tests.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// mobile/e2e/createStory.test.ts (mit Detox)
|
||||
describe('Story Creation Flow', () => {
|
||||
|
|
@ -1530,6 +1568,7 @@ describe('Story Creation Flow', () => {
|
|||
```
|
||||
|
||||
**Backend Integration Tests:**
|
||||
|
||||
```typescript
|
||||
// backend/test/e2e/story.e2e-spec.ts
|
||||
describe('Story API (e2e)', () => {
|
||||
|
|
@ -1580,6 +1619,7 @@ describe('Story API (e2e)', () => {
|
|||
**Problem:** Manual `.env` setup notwendig.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# scripts/setup-dev.sh
|
||||
|
|
@ -1629,6 +1669,7 @@ echo " Mobile: Expo Dev Client wird gestartet..."
|
|||
```
|
||||
|
||||
**VS Code Workspace Settings:**
|
||||
|
||||
```json
|
||||
// .vscode/settings.json
|
||||
{
|
||||
|
|
@ -1684,6 +1725,7 @@ echo " Mobile: Expo Dev Client wird gestartet..."
|
|||
**Problem:** Keine strukturierte Logging-Strategie.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/common/logger/logger.service.ts
|
||||
import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common';
|
||||
|
|
@ -1752,6 +1794,7 @@ export class LoggerService implements NestLoggerService {
|
|||
```
|
||||
|
||||
**Structured Logging in Services:**
|
||||
|
||||
```typescript
|
||||
// story-creation.service.ts
|
||||
this.logger.log('Creating story', 'StoryCreationService', {
|
||||
|
|
@ -1765,6 +1808,7 @@ this.logger.log('Creating story', 'StoryCreationService', {
|
|||
```
|
||||
|
||||
**Log Aggregation (Optional):**
|
||||
|
||||
```typescript
|
||||
// Integration mit Datadog/Sentry
|
||||
import * as Sentry from '@sentry/node';
|
||||
|
|
@ -1791,6 +1835,7 @@ Sentry.init({
|
|||
**Problem:** Manche DTOs haben nur partielle Validierung.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// story/dto/create-story.dto.ts (aktuell nur Zod)
|
||||
import { IsString, IsArray, IsUUID, IsOptional, MaxLength, MinLength } from 'class-validator';
|
||||
|
|
@ -1801,7 +1846,7 @@ export class CreateAnimalStoryDto {
|
|||
description: 'Story description prompt',
|
||||
example: 'A magical adventure in the forest',
|
||||
minLength: 10,
|
||||
maxLength: 2000
|
||||
maxLength: 2000,
|
||||
})
|
||||
@IsString()
|
||||
@MinLength(10, { message: 'Story description must be at least 10 characters' })
|
||||
|
|
@ -1810,14 +1855,14 @@ export class CreateAnimalStoryDto {
|
|||
|
||||
@ApiProperty({
|
||||
description: 'Character ID',
|
||||
example: 'uuid-here'
|
||||
example: 'uuid-here',
|
||||
})
|
||||
@IsUUID('4', { message: 'Invalid character ID format' })
|
||||
characterId: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Optional author ID',
|
||||
required: false
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsUUID('4')
|
||||
|
|
@ -1825,7 +1870,7 @@ export class CreateAnimalStoryDto {
|
|||
|
||||
@ApiProperty({
|
||||
description: 'Optional illustrator ID',
|
||||
required: false
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsUUID('4')
|
||||
|
|
@ -1833,27 +1878,30 @@ export class CreateAnimalStoryDto {
|
|||
}
|
||||
|
||||
// Global Validation Pipe
|
||||
app.useGlobalPipes(new ValidationPipe({
|
||||
whitelist: true, // Strip properties not in DTO
|
||||
forbidNonWhitelisted: true, // Throw error on unknown properties
|
||||
transform: true, // Auto-transform to DTO types
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true
|
||||
},
|
||||
exceptionFactory: (errors) => {
|
||||
const messages = errors.map(error => ({
|
||||
field: error.property,
|
||||
errors: Object.values(error.constraints || {}),
|
||||
}));
|
||||
return new BadRequestException({
|
||||
message: 'Validation failed',
|
||||
errors: messages,
|
||||
});
|
||||
},
|
||||
}));
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true, // Strip properties not in DTO
|
||||
forbidNonWhitelisted: true, // Throw error on unknown properties
|
||||
transform: true, // Auto-transform to DTO types
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
exceptionFactory: (errors) => {
|
||||
const messages = errors.map((error) => ({
|
||||
field: error.property,
|
||||
errors: Object.values(error.constraints || {}),
|
||||
}));
|
||||
return new BadRequestException({
|
||||
message: 'Validation failed',
|
||||
errors: messages,
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
**Vorteile:**
|
||||
|
||||
- 🛡️ Schutz vor Injection-Attacks
|
||||
- ✅ Bessere Fehler-Messages
|
||||
- 📚 Automatische API-Dokumentation (Swagger)
|
||||
|
|
@ -1867,6 +1915,7 @@ app.useGlobalPipes(new ValidationPipe({
|
|||
**Problem:** Keine Filterung von unangemessenen Story-Prompts.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/moderation/moderation.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
|
@ -1931,6 +1980,7 @@ async createStory(@Body() dto: CreateStoryDto) {
|
|||
```
|
||||
|
||||
**Client-Side Handling:**
|
||||
|
||||
```typescript
|
||||
// mobile/app/createStory.tsx
|
||||
try {
|
||||
|
|
@ -1960,6 +2010,7 @@ try {
|
|||
**Problem:** API Keys sind statisch in `.env`.
|
||||
|
||||
**Empfehlung:**
|
||||
|
||||
```typescript
|
||||
// Verwendung von Secret Manager (z.B. Google Secret Manager)
|
||||
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
||||
|
|
@ -1994,6 +2045,7 @@ export default async (): Promise<AppConfig> => {
|
|||
```
|
||||
|
||||
**Alternative: Vault/Doppler**
|
||||
|
||||
```bash
|
||||
# Doppler CLI für lokale Entwicklung
|
||||
npx doppler run -- npm run dev
|
||||
|
|
@ -2015,6 +2067,7 @@ doppler secrets download --no-file --format env > .env.production
|
|||
**Ziel:** 80%+ Coverage für kritische Services
|
||||
|
||||
**Beispiel: Story Creation Service Tests**
|
||||
|
||||
```typescript
|
||||
// backend/src/story/services/story-creation.service.spec.ts
|
||||
describe('StoryCreationService', () => {
|
||||
|
|
@ -2080,10 +2133,12 @@ describe('StoryCreationService', () => {
|
|||
|
||||
supabaseService.getCharacterById.mockResolvedValue(mockCharacter);
|
||||
promptingService.createConsistentCharacterDescriptionPrompts.mockResolvedValue({
|
||||
data: [{
|
||||
characterDescription: 'A friendly brown bear',
|
||||
pages: [1, 2],
|
||||
}],
|
||||
data: [
|
||||
{
|
||||
characterDescription: 'A friendly brown bear',
|
||||
pages: [1, 2],
|
||||
},
|
||||
],
|
||||
error: null,
|
||||
});
|
||||
imageService.generateIllustrationForPage.mockResolvedValue({
|
||||
|
|
@ -2149,14 +2204,16 @@ describe('StoryCreationService', () => {
|
|||
});
|
||||
|
||||
// Should fall back to original character description
|
||||
expect(result.storyData.characters_data[0].character_description)
|
||||
.toBe('Fallback description');
|
||||
expect(result.storyData.characters_data[0].character_description).toBe(
|
||||
'Fallback description'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Coverage Script:**
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
|
|
@ -2194,6 +2251,7 @@ describe('StoryCreationService', () => {
|
|||
**Problem:** Keine Tests für komplette User Journeys.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/test/integration/story-flow.integration.spec.ts
|
||||
describe('Complete Story Creation Flow (Integration)', () => {
|
||||
|
|
@ -2276,7 +2334,7 @@ describe('Complete Story Creation Flow (Integration)', () => {
|
|||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.expect(200);
|
||||
|
||||
const createdStory = getStoriesRes.body.find(s => s.id === storyId);
|
||||
const createdStory = getStoriesRes.body.find((s) => s.id === storyId);
|
||||
expect(createdStory).toBeDefined();
|
||||
expect(createdStory.character_id).toBe(characterId);
|
||||
|
||||
|
|
@ -2309,6 +2367,7 @@ describe('Complete Story Creation Flow (Integration)', () => {
|
|||
**Verbesserung:** Dynamisches Pricing basierend auf Komplexität
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/credits/pricing.service.ts
|
||||
@Injectable()
|
||||
|
|
@ -2400,6 +2459,7 @@ async createStory(params: StoryCreationParams) {
|
|||
```
|
||||
|
||||
**Mobile: Show Price Before Creation**
|
||||
|
||||
```typescript
|
||||
// app/createStory.tsx
|
||||
const [estimatedPrice, setEstimatedPrice] = useState<number>(0);
|
||||
|
|
@ -2441,14 +2501,15 @@ return (
|
|||
|
||||
**Vorschlag:**
|
||||
|
||||
| Tier | Preis/Monat | Credits | Features |
|
||||
|------|-------------|---------|----------|
|
||||
| **Free** | €0 | 5 | - 1 Charakter<br>- Basis-Modell (flux-schnell)<br>- Wasserzeichen |
|
||||
| **Basic** | €9.99 | 50 | - 5 Charaktere<br>- Standard-Modelle<br>- Kein Wasserzeichen |
|
||||
| **Pro** | €19.99 | 150 | - Unlimitierte Charaktere<br>- Premium-Modelle (Flux Pro, Imagen)<br>- Early Access Features<br>- Priority Support |
|
||||
| **Family** | €29.99 | 300 | - Alle Pro Features<br>- Multi-User Accounts (bis zu 5)<br>- Shared Character Library |
|
||||
| Tier | Preis/Monat | Credits | Features |
|
||||
| ---------- | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| **Free** | €0 | 5 | - 1 Charakter<br>- Basis-Modell (flux-schnell)<br>- Wasserzeichen |
|
||||
| **Basic** | €9.99 | 50 | - 5 Charaktere<br>- Standard-Modelle<br>- Kein Wasserzeichen |
|
||||
| **Pro** | €19.99 | 150 | - Unlimitierte Charaktere<br>- Premium-Modelle (Flux Pro, Imagen)<br>- Early Access Features<br>- Priority Support |
|
||||
| **Family** | €29.99 | 300 | - Alle Pro Features<br>- Multi-User Accounts (bis zu 5)<br>- Shared Character Library |
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```typescript
|
||||
// backend/src/subscriptions/subscription.service.ts
|
||||
@Injectable()
|
||||
|
|
@ -2468,20 +2529,20 @@ export class SubscriptionService {
|
|||
|
||||
private getCreditsForTier(tier: string): number {
|
||||
const tiers = {
|
||||
'free': 5,
|
||||
'basic': 50,
|
||||
'pro': 150,
|
||||
'family': 300,
|
||||
free: 5,
|
||||
basic: 50,
|
||||
pro: 150,
|
||||
family: 300,
|
||||
};
|
||||
return tiers[tier] || 0;
|
||||
}
|
||||
|
||||
private getFeaturesForTier(tier: string): string[] {
|
||||
const features = {
|
||||
'free': ['1_character', 'basic_model', 'watermark'],
|
||||
'basic': ['5_characters', 'standard_models', 'no_watermark'],
|
||||
'pro': ['unlimited_characters', 'premium_models', 'early_access', 'priority_support'],
|
||||
'family': ['all_pro', 'multi_user', 'shared_library'],
|
||||
free: ['1_character', 'basic_model', 'watermark'],
|
||||
basic: ['5_characters', 'standard_models', 'no_watermark'],
|
||||
pro: ['unlimited_characters', 'premium_models', 'early_access', 'priority_support'],
|
||||
family: ['all_pro', 'multi_user', 'shared_library'],
|
||||
};
|
||||
return features[tier] || [];
|
||||
}
|
||||
|
|
@ -2497,19 +2558,18 @@ export class SubscriptionService {
|
|||
**Idee:** Credits für Shares/Referrals
|
||||
|
||||
**Lösung:**
|
||||
|
||||
```typescript
|
||||
// backend/src/referrals/referral.service.ts
|
||||
@Injectable()
|
||||
export class ReferralService {
|
||||
async trackShare(userId: string, storyId: string, platform: string): Promise<void> {
|
||||
await this.supabase
|
||||
.from('story_shares')
|
||||
.insert({
|
||||
user_id: userId,
|
||||
story_id: storyId,
|
||||
platform,
|
||||
shared_at: new Date().toISOString(),
|
||||
});
|
||||
await this.supabase.from('story_shares').insert({
|
||||
user_id: userId,
|
||||
story_id: storyId,
|
||||
platform,
|
||||
shared_at: new Date().toISOString(),
|
||||
});
|
||||
|
||||
// Award share credits (1 credit per share, max 5/day)
|
||||
const sharesToday = await this.getSharesCountToday(userId);
|
||||
|
|
@ -2539,6 +2599,7 @@ export class ReferralService {
|
|||
```
|
||||
|
||||
**Mobile: Referral Code System**
|
||||
|
||||
```typescript
|
||||
// app/settings.tsx
|
||||
export default function Settings() {
|
||||
|
|
@ -2578,14 +2639,14 @@ export default function Settings() {
|
|||
|
||||
### 🔴 Kritisch (Sofort angehen)
|
||||
|
||||
| # | Verbesserung | Aufwand | Impact | Grund |
|
||||
|---|--------------|---------|--------|-------|
|
||||
| 1 | Rate Limiting (4.2) | 0.5T | Hoch | Kostenkontrolle, Abuse Prevention |
|
||||
| 2 | Input Validation (7.1) | 1T | Hoch | Security, SQL Injection Prevention |
|
||||
| 3 | Content Moderation (7.2) | 1T | Hoch | Legal Compliance, Brand Safety |
|
||||
| 4 | Image Caching (2.1) | 1T | Hoch | User Experience, Kosten |
|
||||
| 5 | DB Backup Strategy (5.3) | 0.5T | Hoch | Datensicherheit |
|
||||
| 6 | Subscription System (9.2) | 5-7T | Sehr Hoch | Revenue-kritisch |
|
||||
| # | Verbesserung | Aufwand | Impact | Grund |
|
||||
| --- | ------------------------- | ------- | --------- | ---------------------------------- |
|
||||
| 1 | Rate Limiting (4.2) | 0.5T | Hoch | Kostenkontrolle, Abuse Prevention |
|
||||
| 2 | Input Validation (7.1) | 1T | Hoch | Security, SQL Injection Prevention |
|
||||
| 3 | Content Moderation (7.2) | 1T | Hoch | Legal Compliance, Brand Safety |
|
||||
| 4 | Image Caching (2.1) | 1T | Hoch | User Experience, Kosten |
|
||||
| 5 | DB Backup Strategy (5.3) | 0.5T | Hoch | Datensicherheit |
|
||||
| 6 | Subscription System (9.2) | 5-7T | Sehr Hoch | Revenue-kritisch |
|
||||
|
||||
**Gesamt: ~10 Tage**
|
||||
|
||||
|
|
@ -2593,22 +2654,22 @@ export default function Settings() {
|
|||
|
||||
### 🟡 Wichtig (Nächste 1-2 Monate)
|
||||
|
||||
| # | Verbesserung | Aufwand | Impact | Grund |
|
||||
|---|--------------|---------|--------|-------|
|
||||
| 7 | State Management (1.1) | 2-3T | Hoch | Skalierbarkeit |
|
||||
| 8 | Shared Types (1.3) | 2-3T | Hoch | Type Safety, DX |
|
||||
| 9 | AI Caching (2.3) | 2T | Hoch | Kosteneinsparung |
|
||||
| 10 | Progress Tracking (2.4) | 3T | Hoch | UX |
|
||||
| 11 | Onboarding (3.1) | 2T | Hoch | User Retention |
|
||||
| 12 | Story Sharing (3.2) | 2T | Hoch | Growth |
|
||||
| 13 | Immersive Reading (3.4) | 3-4T | Hoch | UX |
|
||||
| 14 | API Standardisierung (4.1) | 1T | Mittel | DX, Maintainability |
|
||||
| 15 | Webhooks (4.4) | 2-3T | Hoch | UX |
|
||||
| 16 | JSONB Auto-Sync (5.1) | 1T | Mittel | Data Consistency |
|
||||
| 17 | Logging System (6.3) | 1T | Mittel | Debugging, Monitoring |
|
||||
| 18 | Unit Tests (8.1) | 5-7T | Hoch | Code Quality |
|
||||
| 19 | Dynamic Pricing (9.1) | 2T | Hoch | Revenue Optimization |
|
||||
| 20 | Referral System (9.3) | 3T | Hoch | Growth |
|
||||
| # | Verbesserung | Aufwand | Impact | Grund |
|
||||
| --- | -------------------------- | ------- | ------ | --------------------- |
|
||||
| 7 | State Management (1.1) | 2-3T | Hoch | Skalierbarkeit |
|
||||
| 8 | Shared Types (1.3) | 2-3T | Hoch | Type Safety, DX |
|
||||
| 9 | AI Caching (2.3) | 2T | Hoch | Kosteneinsparung |
|
||||
| 10 | Progress Tracking (2.4) | 3T | Hoch | UX |
|
||||
| 11 | Onboarding (3.1) | 2T | Hoch | User Retention |
|
||||
| 12 | Story Sharing (3.2) | 2T | Hoch | Growth |
|
||||
| 13 | Immersive Reading (3.4) | 3-4T | Hoch | UX |
|
||||
| 14 | API Standardisierung (4.1) | 1T | Mittel | DX, Maintainability |
|
||||
| 15 | Webhooks (4.4) | 2-3T | Hoch | UX |
|
||||
| 16 | JSONB Auto-Sync (5.1) | 1T | Mittel | Data Consistency |
|
||||
| 17 | Logging System (6.3) | 1T | Mittel | Debugging, Monitoring |
|
||||
| 18 | Unit Tests (8.1) | 5-7T | Hoch | Code Quality |
|
||||
| 19 | Dynamic Pricing (9.1) | 2T | Hoch | Revenue Optimization |
|
||||
| 20 | Referral System (9.3) | 3T | Hoch | Growth |
|
||||
|
||||
**Gesamt: ~35-40 Tage**
|
||||
|
||||
|
|
@ -2616,16 +2677,16 @@ export default function Settings() {
|
|||
|
||||
### 🟢 Nice-to-Have (Backlog)
|
||||
|
||||
| # | Verbesserung | Aufwand | Impact | Anmerkung |
|
||||
|---|--------------|---------|--------|-----------|
|
||||
| 21 | Monorepo Optimization (1.2) | 1T | Mittel | DX |
|
||||
| 22 | FlashList (2.2) | 0.5T | Mittel | Performance |
|
||||
| 23 | Offline Mode (3.3) | 2T | Mittel | Edge Case |
|
||||
| 24 | Character Filters (3.5) | 1-2T | Mittel | UX |
|
||||
| 25 | Health Checks (4.3) | 1T | Mittel | Ops |
|
||||
| 26 | Full-Text Search (5.2) | 1T | Mittel | UX |
|
||||
| 27 | Dev Setup Script (6.2) | 0.5T | Mittel | DX |
|
||||
| 28 | API Key Rotation (7.3) | 1T | Hoch | Security (long-term) |
|
||||
| # | Verbesserung | Aufwand | Impact | Anmerkung |
|
||||
| --- | --------------------------- | ------- | ------ | -------------------- |
|
||||
| 21 | Monorepo Optimization (1.2) | 1T | Mittel | DX |
|
||||
| 22 | FlashList (2.2) | 0.5T | Mittel | Performance |
|
||||
| 23 | Offline Mode (3.3) | 2T | Mittel | Edge Case |
|
||||
| 24 | Character Filters (3.5) | 1-2T | Mittel | UX |
|
||||
| 25 | Health Checks (4.3) | 1T | Mittel | Ops |
|
||||
| 26 | Full-Text Search (5.2) | 1T | Mittel | UX |
|
||||
| 27 | Dev Setup Script (6.2) | 0.5T | Mittel | DX |
|
||||
| 28 | API Key Rotation (7.3) | 1T | Hoch | Security (long-term) |
|
||||
|
||||
**Gesamt: ~10 Tage**
|
||||
|
||||
|
|
@ -2634,6 +2695,7 @@ export default function Settings() {
|
|||
## Zusammenfassung
|
||||
|
||||
### Quick Wins (< 1 Tag, Hoher Impact)
|
||||
|
||||
1. ✅ Rate Limiting (0.5T)
|
||||
2. ✅ Input Validation (1T)
|
||||
3. ✅ Content Moderation (1T)
|
||||
|
|
@ -2645,6 +2707,7 @@ export default function Settings() {
|
|||
---
|
||||
|
||||
### MVP Next Steps (1-2 Wochen)
|
||||
|
||||
1. Subscription System implementieren (5-7T)
|
||||
2. State Management modernisieren (2-3T)
|
||||
3. AI Caching aufsetzen (2T)
|
||||
|
|
@ -2655,6 +2718,7 @@ export default function Settings() {
|
|||
---
|
||||
|
||||
### Long-Term Roadmap (3-6 Monate)
|
||||
|
||||
1. **Q1:** Kritische Verbesserungen + Subscription
|
||||
2. **Q2:** UX Verbesserungen + Testing
|
||||
3. **Q3:** Growth Features (Referrals, Sharing)
|
||||
|
|
@ -2687,4 +2751,4 @@ Dieses Dokument sollte als lebendes Dokument behandelt werden. Nach Implementier
|
|||
|
||||
**Ende des Dokuments**
|
||||
|
||||
*Erstellt mit ❤️ für Märchenzauber*
|
||||
_Erstellt mit ❤️ für Märchenzauber_
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@
|
|||
## 🔴 What We Tried (That Didn't Work)
|
||||
|
||||
### Attempt 1: Git Config URL Rewriting
|
||||
|
||||
**What we did:**
|
||||
|
||||
```yaml
|
||||
- name: Configure git for private packages
|
||||
run: |
|
||||
|
|
@ -13,32 +15,41 @@
|
|||
```
|
||||
|
||||
**Why it failed:**
|
||||
|
||||
- npm ci reads URLs directly from `package-lock.json`
|
||||
- npm's internal git client doesn't reliably honor git config `url.insteadOf` rules
|
||||
- npm fell back to SSH when it couldn't authenticate via HTTPS
|
||||
|
||||
### Attempt 2: Invalid .npmrc Configuration
|
||||
|
||||
**What we did:**
|
||||
|
||||
```
|
||||
# backend/.npmrc
|
||||
git-ssh-url = https://github.com/
|
||||
```
|
||||
|
||||
**Why it failed:**
|
||||
|
||||
- `git-ssh-url` is not a valid npm configuration option
|
||||
- npm doesn't recognize this setting
|
||||
|
||||
### Attempt 3: Reordering Steps (Git Config Before Node Setup)
|
||||
|
||||
**What we did:**
|
||||
|
||||
- Moved git config step before `actions/setup-node`
|
||||
- Added npm cache clearing
|
||||
|
||||
**Why it failed:**
|
||||
|
||||
- While this helped with caching issues, it didn't solve the root problem
|
||||
- npm ci still ignored git config and used lockfile URLs directly
|
||||
|
||||
### Attempt 4: persist-credentials: false
|
||||
|
||||
**What we did:**
|
||||
|
||||
```yaml
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -46,11 +57,14 @@ git-ssh-url = https://github.com/
|
|||
```
|
||||
|
||||
**Why it failed (partially):**
|
||||
|
||||
- This fixed the token override issue (actions/checkout was setting default GITHUB_TOKEN)
|
||||
- But npm ci still couldn't authenticate because the lockfile URL had no token
|
||||
|
||||
### Attempt 5: Local Git Config Override
|
||||
|
||||
**What we did:**
|
||||
|
||||
```bash
|
||||
# Try to override global SSH rewrites with local HTTPS config
|
||||
cd project-root
|
||||
|
|
@ -61,6 +75,7 @@ npm install
|
|||
```
|
||||
|
||||
**Why it failed:**
|
||||
|
||||
- npm's internal git client doesn't consistently honor local git config during package resolution
|
||||
- Even with correct local config, npm still generated SSH URLs in `package-lock.json`
|
||||
- The git config order of precedence (local > global > system) doesn't reliably apply to npm's git subprocess
|
||||
|
|
@ -72,19 +87,23 @@ npm install
|
|||
## ✅ What Finally Worked
|
||||
|
||||
### The Root Cause
|
||||
|
||||
**npm ci uses URLs from package-lock.json directly and ignores git config url.insteadOf**
|
||||
|
||||
Even though:
|
||||
|
||||
- ✅ package.json had `git+https://github.com/...`
|
||||
- ✅ package-lock.json had `git+https://github.com/...` (no SSH)
|
||||
- ✅ git config was properly set
|
||||
|
||||
npm would run:
|
||||
|
||||
```bash
|
||||
git ls-remote https://github.com/Memo-2023/mana-core-nestjs-package.git
|
||||
```
|
||||
|
||||
This failed with "Permission denied" because:
|
||||
|
||||
1. The URL had no authentication token
|
||||
2. npm fell back to SSH (which wasn't configured)
|
||||
3. Git config rewrites were ignored by npm's git subprocess
|
||||
|
|
@ -92,13 +111,15 @@ This failed with "Permission denied" because:
|
|||
### The Solution: Runtime Token Injection
|
||||
|
||||
**Step 1: Prevent Token Override**
|
||||
|
||||
```yaml
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false # Don't let default GITHUB_TOKEN interfere
|
||||
persist-credentials: false # Don't let default GITHUB_TOKEN interfere
|
||||
```
|
||||
|
||||
**Step 2a: If lockfile has HTTPS URLs - Verify and Inject Token**
|
||||
|
||||
```yaml
|
||||
- name: Verify no SSH URLs in lockfile
|
||||
run: |
|
||||
|
|
@ -116,6 +137,7 @@ This failed with "Permission denied" because:
|
|||
```
|
||||
|
||||
**Step 2b: If lockfile has SSH URLs - Patch to HTTPS with Token**
|
||||
|
||||
```yaml
|
||||
- name: Patch SSH URLs to HTTPS with token
|
||||
env:
|
||||
|
|
@ -135,6 +157,7 @@ This failed with "Permission denied" because:
|
|||
> **Note**: Use Step 2a if you can generate HTTPS lockfiles locally. Use Step 2b if your local git config always generates SSH URLs (accepts either format).
|
||||
|
||||
**Step 4: Run npm ci**
|
||||
|
||||
```yaml
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
|
@ -148,6 +171,7 @@ This failed with "Permission denied" because:
|
|||
4. **Consistent Behavior**: Works across npm v7, v8, v9+ and all CI environments
|
||||
|
||||
### The Execution Order (Critical)
|
||||
|
||||
```
|
||||
1. Checkout (persist-credentials: false)
|
||||
2. Configure git (defense in depth, may help in edge cases)
|
||||
|
|
@ -161,24 +185,26 @@ This failed with "Permission denied" because:
|
|||
|
||||
## 📊 Key Learnings
|
||||
|
||||
| Issue | What We Learned |
|
||||
|-------|----------------|
|
||||
| Git config ignored | npm ci reads package-lock.json directly, bypasses git config |
|
||||
| persist-credentials | Default checkout token can override custom PAT config |
|
||||
| npm v7+ behavior | Modern npm converts HTTPS to SSH for private repos if auth fails |
|
||||
| Token placement | Must inject token into lockfile URL, not just configure git |
|
||||
| sed is reliable | Runtime patching with sed is more reliable than git rewrites |
|
||||
| Issue | What We Learned |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Git config ignored | npm ci reads package-lock.json directly, bypasses git config |
|
||||
| persist-credentials | Default checkout token can override custom PAT config |
|
||||
| npm v7+ behavior | Modern npm converts HTTPS to SSH for private repos if auth fails |
|
||||
| Token placement | Must inject token into lockfile URL, not just configure git |
|
||||
| sed is reliable | Runtime patching with sed is more reliable than git rewrites |
|
||||
| Local git config fails | npm's git subprocess doesn't honor local config consistently - even `git ls-remote` works but `npm install` still writes SSH |
|
||||
| Don't fight it locally | Stop trying to generate HTTPS lockfiles locally - accept SSH and patch in CI |
|
||||
| Don't fight it locally | Stop trying to generate HTTPS lockfiles locally - accept SSH and patch in CI |
|
||||
|
||||
## 🎯 The One-Liner That Fixed It
|
||||
|
||||
**For HTTPS lockfiles:**
|
||||
|
||||
```bash
|
||||
sed -i "s|https://github.com/Memo-2023/|https://${GH_TOKEN}@github.com/Memo-2023/|g" package-lock.json
|
||||
```
|
||||
|
||||
**For SSH lockfiles (if your local git config generates SSH URLs):**
|
||||
|
||||
```bash
|
||||
sed -i "s|git+ssh://git@github.com/Memo-2023/|git+https://${GH_TOKEN}@github.com/Memo-2023/|g" package-lock.json
|
||||
```
|
||||
|
|
@ -267,6 +293,7 @@ Make sure you have set up in your repository:
|
|||
If your local git config **always** generates SSH URLs in `package-lock.json` (despite attempts to fix it locally), you have two options:
|
||||
|
||||
### Option 1: Accept SSH and Patch in CI (Recommended)
|
||||
|
||||
1. **Stop fighting with local git config** - it's unreliable
|
||||
2. **Commit the lockfile with SSH URLs** as-is
|
||||
3. **Use the flexible sed command** in CI that handles both SSH and HTTPS:
|
||||
|
|
@ -285,6 +312,7 @@ If your local git config **always** generates SSH URLs in `package-lock.json` (d
|
|||
```
|
||||
|
||||
### Option 2: Force HTTPS Locally (Fragile)
|
||||
|
||||
If you insist on HTTPS lockfiles, you must ensure NO global git config rewrites exist:
|
||||
|
||||
```bash
|
||||
|
|
@ -307,4 +335,4 @@ npm install
|
|||
**Date Fixed:** 2025-09-29
|
||||
**Commit:** `5cce473` - fix: inject PAT token into package-lock.json for npm ci
|
||||
|
||||
**Updated:** 2025-09-30 - Added SSH lockfile handling documentation
|
||||
**Updated:** 2025-09-30 - Added SSH lockfile handling documentation
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
Dieses Dokument beschreibt das Upgrade von **Expo SDK 51** (aktuell) auf **Expo SDK 54** für das Märchenzauber Projekt.
|
||||
|
||||
### Aktuelle Version
|
||||
|
||||
- **Expo SDK**: 51.0.28
|
||||
- **React Native**: 0.74.5
|
||||
- **React**: 18.2.0
|
||||
|
||||
### Zielversion
|
||||
|
||||
- **Expo SDK**: 54.0.0
|
||||
- **React Native**: 0.81.0
|
||||
- **React**: 19.1.0
|
||||
|
|
@ -17,20 +19,24 @@ Dieses Dokument beschreibt das Upgrade von **Expo SDK 51** (aktuell) auf **Expo
|
|||
## 🚀 Neue Features in SDK 54
|
||||
|
||||
### 1. Performance-Verbesserungen
|
||||
|
||||
- **Precompiled XCFrameworks für iOS**: Reduziert Clean-Build-Zeiten von ~120 auf ~10 Sekunden
|
||||
- **Schnellere Builds**: Besonders bei großen Projekten spürbar
|
||||
|
||||
### 2. Native UI-Features
|
||||
|
||||
- **Native Tabs (Beta)**: Liquid Glass Tabs mit automatischem Scrolling
|
||||
- **iOS 26 Support**: Liquid Glass Icons und Views
|
||||
- **Verbesserte Modals**: Web-Modals verhalten sich jetzt wie iPad/iPhone Modals
|
||||
|
||||
### 3. Technische Updates
|
||||
|
||||
- **React Native 0.81** mit React 19.1
|
||||
- **Android 16 Target**: Edge-to-Edge standardmäßig aktiviert
|
||||
- **New Architecture Migration**: SDK 55 wird nur noch New Architecture unterstützen
|
||||
|
||||
### 4. API-Verbesserungen
|
||||
|
||||
- **File System API**: Neue objektorientierte API
|
||||
- **SQLite localStorage**: Drop-in Ersatz für Web localStorage
|
||||
- **Streaming Support**: TextDecoderStream/TextEncoderStream für AI-Integration
|
||||
|
|
@ -40,14 +46,17 @@ Dieses Dokument beschreibt das Upgrade von **Expo SDK 51** (aktuell) auf **Expo
|
|||
### 1. Kritische Änderungen
|
||||
|
||||
#### expo-av Deprecation
|
||||
|
||||
```diff
|
||||
- import { Audio, Video } from 'expo-av';
|
||||
+ import { Audio } from 'expo-audio';
|
||||
+ import { Video } from 'expo-video';
|
||||
```
|
||||
|
||||
**Wichtig**: expo-av wird in SDK 55 entfernt!
|
||||
|
||||
#### File System API
|
||||
|
||||
```diff
|
||||
- import { ... } from 'expo-file-system/next';
|
||||
+ import { ... } from 'expo-file-system';
|
||||
|
|
@ -56,22 +65,26 @@ Dieses Dokument beschreibt das Upgrade von **Expo SDK 51** (aktuell) auf **Expo
|
|||
```
|
||||
|
||||
#### Reanimated v4
|
||||
|
||||
- Nur New Architecture Support
|
||||
- Für Legacy Architecture: Bei Reanimated v3 bleiben
|
||||
|
||||
### 2. Platform-spezifische Änderungen
|
||||
|
||||
#### iOS
|
||||
|
||||
- **Xcode Requirement**: Xcode 16.1+ (empfohlen: Xcode 26)
|
||||
- **iOS Minimum**: iOS 15.1
|
||||
|
||||
#### Android
|
||||
|
||||
- **Target SDK**: Android 16
|
||||
- **Edge-to-Edge**: Standardmäßig aktiviert (nicht deaktivierbar)
|
||||
|
||||
## 📝 Upgrade-Schritte
|
||||
|
||||
### Schritt 1: Backup erstellen
|
||||
|
||||
```bash
|
||||
# Git-Status prüfen
|
||||
git status
|
||||
|
|
@ -87,6 +100,7 @@ git checkout -b upgrade/expo-sdk-54
|
|||
### Schritt 2: Dependencies aktualisieren
|
||||
|
||||
#### Mobile App (apps/mobile/package.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
|
|
@ -127,6 +141,7 @@ git checkout -b upgrade/expo-sdk-54
|
|||
```
|
||||
|
||||
### Schritt 3: Installation durchführen
|
||||
|
||||
```bash
|
||||
# Cache löschen
|
||||
cd apps/mobile
|
||||
|
|
@ -141,6 +156,7 @@ npx expo-doctor
|
|||
```
|
||||
|
||||
### Schritt 4: Native Directories zurücksetzen (wenn vorhanden)
|
||||
|
||||
```bash
|
||||
# Alte native Directories löschen
|
||||
rm -rf ios android
|
||||
|
|
@ -152,6 +168,7 @@ npx expo prebuild --clean
|
|||
### Schritt 5: Code-Anpassungen
|
||||
|
||||
#### 1. File System API migrieren
|
||||
|
||||
```typescript
|
||||
// ALT (SDK 51)
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
|
|
@ -164,12 +181,14 @@ import * as FileSystem from 'expo-file-system/legacy';
|
|||
```
|
||||
|
||||
#### 2. Router Updates (v3 → v4)
|
||||
|
||||
```typescript
|
||||
// Prüfe expo-router Imports und Konfiguration
|
||||
// Router v4 hat verbesserte TypeScript-Unterstützung
|
||||
```
|
||||
|
||||
#### 3. Native Tabs (Optional - Beta)
|
||||
|
||||
```typescript
|
||||
// Neue native Tabs verwenden (Beta)
|
||||
import { Tabs } from 'expo-router/unstable-native-tabs';
|
||||
|
|
@ -178,6 +197,7 @@ import { Tabs } from 'expo-router/unstable-native-tabs';
|
|||
```
|
||||
|
||||
### Schritt 6: Metro-Konfiguration prüfen
|
||||
|
||||
```javascript
|
||||
// metro.config.js
|
||||
// Entferne veraltete Overrides
|
||||
|
|
@ -185,6 +205,7 @@ import { Tabs } from 'expo-router/unstable-native-tabs';
|
|||
```
|
||||
|
||||
### Schritt 7: Tests durchführen
|
||||
|
||||
```bash
|
||||
# Unit Tests
|
||||
npm test
|
||||
|
|
@ -205,24 +226,29 @@ npm run android
|
|||
## 🔧 Spezifische Anpassungen für Märchenzauber
|
||||
|
||||
### 1. Image Generation
|
||||
|
||||
- Replicate Integration sollte weiterhin funktionieren
|
||||
- Prüfe expo-image v2.0 Breaking Changes
|
||||
|
||||
### 2. Supabase Integration
|
||||
|
||||
- Keine direkten Änderungen erwartet
|
||||
- Auth-Flow testen
|
||||
|
||||
### 3. Character/Story Features
|
||||
|
||||
- File System API für Caching anpassen
|
||||
- Image Picker API prüfen
|
||||
|
||||
### 4. Performance-Optimierungen nutzen
|
||||
|
||||
- iOS Builds werden deutlich schneller
|
||||
- Android Edge-to-Edge für bessere UX
|
||||
|
||||
## 📱 Neue Features für Märchenzauber nutzen
|
||||
|
||||
### Native Tabs für Story-Navigation
|
||||
|
||||
```typescript
|
||||
import { Tabs } from 'expo-router/unstable-native-tabs';
|
||||
|
||||
|
|
@ -240,6 +266,7 @@ import { Tabs } from 'expo-router/unstable-native-tabs';
|
|||
```
|
||||
|
||||
### iOS 26 Liquid Glass Icons
|
||||
|
||||
```json
|
||||
// app.json
|
||||
{
|
||||
|
|
@ -252,6 +279,7 @@ import { Tabs } from 'expo-router/unstable-native-tabs';
|
|||
```
|
||||
|
||||
### Verbesserte Modal-Präsentation
|
||||
|
||||
```typescript
|
||||
// Web-Modals verhalten sich jetzt nativ
|
||||
<Modal
|
||||
|
|
@ -265,6 +293,7 @@ import { Tabs } from 'expo-router/unstable-native-tabs';
|
|||
## 🐛 Troubleshooting
|
||||
|
||||
### Problem: Build-Fehler nach Upgrade
|
||||
|
||||
```bash
|
||||
# Cache komplett zurücksetzen
|
||||
npx expo start -c
|
||||
|
|
@ -273,12 +302,14 @@ watchman watch-del-all
|
|||
```
|
||||
|
||||
### Problem: Reanimated Fehler
|
||||
|
||||
```bash
|
||||
# Bei Legacy Architecture bleiben
|
||||
npm install react-native-reanimated@3.16.0
|
||||
```
|
||||
|
||||
### Problem: Metro-Fehler
|
||||
|
||||
```javascript
|
||||
// metro.config.js anpassen
|
||||
// Entferne metro/src/... Imports
|
||||
|
|
@ -286,6 +317,7 @@ npm install react-native-reanimated@3.16.0
|
|||
```
|
||||
|
||||
### Problem: Type-Fehler mit React 19
|
||||
|
||||
```bash
|
||||
# TypeScript-Definitionen aktualisieren
|
||||
npm install --save-dev @types/react@~19.1.0
|
||||
|
|
@ -340,4 +372,4 @@ npm install --save-dev @types/react@~19.1.0
|
|||
---
|
||||
|
||||
**Letzte Aktualisierung**: Januar 2025
|
||||
**Märchenzauber Version**: Nach Upgrade auf SDK 54
|
||||
**Märchenzauber Version**: Nach Upgrade auf SDK 54
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ npx expo install @expo/ui
|
|||
### Host Component
|
||||
|
||||
Die `Host`-Komponente ist der Container für alle Expo UI Komponenten. Sie funktioniert ähnlich wie:
|
||||
|
||||
- `<svg>` für DOM
|
||||
- `<Canvas>` für react-native-skia
|
||||
|
||||
|
|
@ -30,14 +31,13 @@ Unter der Haube verwendet sie `UIHostingController` um SwiftUI Views in UIKit zu
|
|||
```javascript
|
||||
import { Host } from '@expo/ui/swift-ui';
|
||||
|
||||
<Host style={{ flex: 1 }}>
|
||||
{/* Expo UI Komponenten hier */}
|
||||
</Host>
|
||||
<Host style={{ flex: 1 }}>{/* Expo UI Komponenten hier */}</Host>;
|
||||
```
|
||||
|
||||
### 1-zu-1 Mapping
|
||||
|
||||
Expo UI bietet eine direkte Abbildung zu nativen UI-Frameworks:
|
||||
|
||||
- SwiftUI Views für iOS
|
||||
- Jetpack Compose Components für Android
|
||||
|
||||
|
|
@ -45,26 +45,26 @@ Expo UI bietet eine direkte Abbildung zu nativen UI-Frameworks:
|
|||
|
||||
### SwiftUI Components (@expo/ui/swift-ui)
|
||||
|
||||
| Komponente | Beschreibung |
|
||||
|------------|-------------|
|
||||
| `Host` | Container für SwiftUI Komponenten |
|
||||
| `Button` | Native Button mit System-Images |
|
||||
| `Text` | Native Text-Komponente |
|
||||
| `VStack` | Vertikales Layout |
|
||||
| `ContextMenu` | Kontextmenü |
|
||||
| `DateTimePicker` | Datum/Zeit-Auswahl |
|
||||
| `Picker` | Segmented und Wheel-Varianten |
|
||||
| `Slider` | Schieberegler |
|
||||
| `Switch` | Toggle/Schalter |
|
||||
| `LinearProgress` | Fortschrittsbalken |
|
||||
| `BottomSheet` | Bottom Sheet Modal |
|
||||
| Komponente | Beschreibung |
|
||||
| ---------------- | --------------------------------- |
|
||||
| `Host` | Container für SwiftUI Komponenten |
|
||||
| `Button` | Native Button mit System-Images |
|
||||
| `Text` | Native Text-Komponente |
|
||||
| `VStack` | Vertikales Layout |
|
||||
| `ContextMenu` | Kontextmenü |
|
||||
| `DateTimePicker` | Datum/Zeit-Auswahl |
|
||||
| `Picker` | Segmented und Wheel-Varianten |
|
||||
| `Slider` | Schieberegler |
|
||||
| `Switch` | Toggle/Schalter |
|
||||
| `LinearProgress` | Fortschrittsbalken |
|
||||
| `BottomSheet` | Bottom Sheet Modal |
|
||||
|
||||
### Jetpack Compose Components (@expo/ui/jetpack-compose)
|
||||
|
||||
| Komponente | Beschreibung |
|
||||
|------------|-------------|
|
||||
| `TextInput` | Native Texteingabe |
|
||||
| `Button` | Native Android Button |
|
||||
| Komponente | Beschreibung |
|
||||
| ---------------------------------- | --------------------- |
|
||||
| `TextInput` | Native Texteingabe |
|
||||
| `Button` | Native Android Button |
|
||||
| Weitere Komponenten in Entwicklung |
|
||||
|
||||
## Code-Beispiele
|
||||
|
|
@ -75,16 +75,14 @@ Expo UI bietet eine direkte Abbildung zu nativen UI-Frameworks:
|
|||
import { Host, VStack, Text, Button } from '@expo/ui/swift-ui';
|
||||
|
||||
function MyComponent() {
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<VStack spacing={8}>
|
||||
<Text>Willkommen bei Expo UI!</Text>
|
||||
<Button onPress={() => console.log('Geklickt!')}>
|
||||
Klick mich
|
||||
</Button>
|
||||
</VStack>
|
||||
</Host>
|
||||
);
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<VStack spacing={8}>
|
||||
<Text>Willkommen bei Expo UI!</Text>
|
||||
<Button onPress={() => console.log('Geklickt!')}>Klick mich</Button>
|
||||
</VStack>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -96,19 +94,16 @@ import { useState } from 'react';
|
|||
import { useWindowDimensions } from 'react-native';
|
||||
|
||||
function BottomSheetExample() {
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const { width } = useWindowDimensions();
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
return (
|
||||
<Host style={{ position: 'absolute', width }}>
|
||||
<BottomSheet
|
||||
isOpened={isOpened}
|
||||
onIsOpenedChange={e => setIsOpened(e)}
|
||||
>
|
||||
<Text>Sheet-Inhalt hier</Text>
|
||||
</BottomSheet>
|
||||
</Host>
|
||||
);
|
||||
return (
|
||||
<Host style={{ position: 'absolute', width }}>
|
||||
<BottomSheet isOpened={isOpened} onIsOpenedChange={(e) => setIsOpened(e)}>
|
||||
<Text>Sheet-Inhalt hier</Text>
|
||||
</BottomSheet>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -118,15 +113,9 @@ function BottomSheetExample() {
|
|||
import { TextInput } from '@expo/ui/jetpack-compose';
|
||||
|
||||
function AndroidInput() {
|
||||
const [value, setValue] = useState('');
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
autocorrection={false}
|
||||
defaultValue="Standardtext"
|
||||
onChangeText={setValue}
|
||||
/>
|
||||
);
|
||||
return <TextInput autocorrection={false} defaultValue="Standardtext" onChangeText={setValue} />;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -141,16 +130,16 @@ import { Host, VStack } from '@expo/ui/swift-ui';
|
|||
import { View, Text as RNText } from 'react-native';
|
||||
|
||||
function MixedComponent() {
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<VStack>
|
||||
{/* React Native Komponente innerhalb von Expo UI */}
|
||||
<View>
|
||||
<RNText>React Native Text</RNText>
|
||||
</View>
|
||||
</VStack>
|
||||
</Host>
|
||||
);
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<VStack>
|
||||
{/* React Native Komponente innerhalb von Expo UI */}
|
||||
<View>
|
||||
<RNText>React Native Text</RNText>
|
||||
</View>
|
||||
</VStack>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -159,21 +148,25 @@ Expo UI erstellt automatisch einen `UIViewRepresentable` Wrapper für React Nati
|
|||
## Vorteile von Expo UI
|
||||
|
||||
### 1. Native Performance
|
||||
|
||||
- Direkte Nutzung von SwiftUI und Jetpack Compose
|
||||
- Keine Bridge-Overhead für UI-Updates
|
||||
- Optimierte native Animationen und Transitions
|
||||
|
||||
### 2. Moderne UI-Patterns
|
||||
|
||||
- Zugriff auf die neuesten nativen UI-Features
|
||||
- System-konsistentes Design out-of-the-box
|
||||
- Native Gesten und Interaktionen
|
||||
|
||||
### 3. Flexibilität
|
||||
|
||||
- Full-App Support: Gesamte App in Expo UI schreibbar
|
||||
- Component-Level Mixing: Mische React Native, Expo UI, DOM und Custom 2D Components
|
||||
- Schrittweise Migration möglich
|
||||
|
||||
### 4. Developer Experience
|
||||
|
||||
- TypeScript Support mit vollständigen Typen
|
||||
- Hot Reload Support
|
||||
- Vertraute React-Patterns
|
||||
|
|
@ -200,13 +193,11 @@ Expo UI erstellt automatisch einen `UIViewRepresentable` Wrapper für React Nati
|
|||
```javascript
|
||||
// Separiere native UI in eigene Komponenten
|
||||
function NativeSheet({ children }) {
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<BottomSheet>
|
||||
{children}
|
||||
</BottomSheet>
|
||||
</Host>
|
||||
);
|
||||
return (
|
||||
<Host style={{ flex: 1 }}>
|
||||
<BottomSheet>{children}</BottomSheet>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -216,8 +207,8 @@ function NativeSheet({ children }) {
|
|||
import { Platform } from 'react-native';
|
||||
|
||||
const NativeButton = Platform.select({
|
||||
ios: require('@expo/ui/swift-ui').Button,
|
||||
android: require('@expo/ui/jetpack-compose').Button,
|
||||
ios: require('@expo/ui/swift-ui').Button,
|
||||
android: require('@expo/ui/jetpack-compose').Button,
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -227,7 +218,7 @@ const NativeButton = Platform.select({
|
|||
import type { ButtonProps } from '@expo/ui/swift-ui';
|
||||
|
||||
interface MyButtonProps extends ButtonProps {
|
||||
variant?: 'primary' | 'secondary';
|
||||
variant?: 'primary' | 'secondary';
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -257,15 +248,15 @@ interface MyButtonProps extends ButtonProps {
|
|||
import { TouchableOpacity, Text } from 'react-native';
|
||||
|
||||
<TouchableOpacity onPress={handlePress}>
|
||||
<Text>Click me</Text>
|
||||
</TouchableOpacity>
|
||||
<Text>Click me</Text>
|
||||
</TouchableOpacity>;
|
||||
|
||||
// Nachher (Expo UI)
|
||||
import { Host, Button } from '@expo/ui/swift-ui';
|
||||
|
||||
<Host>
|
||||
<Button onPress={handlePress}>Click me</Button>
|
||||
</Host>
|
||||
<Button onPress={handlePress}>Click me</Button>
|
||||
</Host>;
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
|
@ -273,26 +264,32 @@ import { Host, Button } from '@expo/ui/swift-ui';
|
|||
### Häufige Probleme
|
||||
|
||||
**Problem**: Components werden nicht angezeigt
|
||||
|
||||
- **Lösung**: Stelle sicher, dass Components in einem `Host` gewrappt sind
|
||||
|
||||
**Problem**: Build-Fehler mit Expo Go
|
||||
|
||||
- **Lösung**: Verwende Development Build: `npx expo run:ios` oder `npx expo run:android`
|
||||
|
||||
**Problem**: TypeScript-Fehler
|
||||
|
||||
- **Lösung**: Update auf neueste @expo/ui Version
|
||||
|
||||
## Ressourcen
|
||||
|
||||
### Offizielle Dokumentation
|
||||
|
||||
- [Expo UI Docs](https://docs.expo.dev/versions/latest/sdk/ui/)
|
||||
- [SwiftUI Guide](https://docs.expo.dev/guides/expo-ui-swift-ui/)
|
||||
- [GitHub Repository](https://github.com/expo/expo/tree/main/packages/expo-ui)
|
||||
|
||||
### Community
|
||||
|
||||
- [Expo Discord](https://discord.gg/expo)
|
||||
- [GitHub Discussions](https://github.com/expo/expo/discussions)
|
||||
|
||||
### Beispiel-Projekte
|
||||
|
||||
- [Liquid Glass App](https://expo.dev/blog/liquid-glass-app-with-expo-ui-and-swiftui)
|
||||
- [Live Activity Example](https://christopher.engineering/en/blog/live-activity-with-react-native/)
|
||||
|
||||
|
|
@ -301,7 +298,8 @@ import { Host, Button } from '@expo/ui/swift-ui';
|
|||
Expo UI repräsentiert einen innovativen Ansatz für React Native Development, der die Grenzen zwischen React Native und nativen UI-Frameworks verschwimmen lässt. Während es sich noch in der Alpha-Phase befindet, zeigt es bereits das Potenzial für die Zukunft der mobilen App-Entwicklung mit React Native.
|
||||
|
||||
Für das Märchenzauber-Projekt könnte Expo UI besonders interessant sein für:
|
||||
|
||||
- Native Picker-Komponenten für Sprachauswahl
|
||||
- BottomSheets für Story-Optionen
|
||||
- Native Animationen für magische Übergänge
|
||||
- System-konsistente UI-Elemente
|
||||
- System-konsistente UI-Elemente
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue