managarten/apps/maerchenzauber/CHARACTER_SHARING_IMPLEMENTATION.md
Wuesteon d36b321d9d 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)
2025-11-27 18:33:16 +01:00

11 KiB

Character Sharing Feature - Implementation Complete

Overview

Users can now share private characters with friends via deep links. Characters remain private to the owner, but anyone with the link can import them to their library for 10 credits.

Key Concept: This is private sharing - no publishing required. Your character stays private, only people with the link can see and import it.

Important: Private Sharing Model

Unlike typical social features, this implementation prioritizes privacy:

  • No Publishing Required - Share any character instantly
  • Stays Private - Your character remains yours, not in public galleries
  • Link-Only Access - Only people with the deep link can view/import
  • Full Control - Delete the character = link stops working
  • No Public Discovery - Characters aren't searchable or browsable
  • No Analytics - You won't see who clicked your link (privacy-first)

Use Case: Share a character privately with a friend via Messages/WhatsApp, not publish to the world.

What Was Implemented

Backend Changes (NestJS)

  1. New DTO (backend/src/character/dto/import-character.dto.ts)

    • Validation for character import requests
  2. Service Methods (backend/src/core/services/supabase-jsonb-auth.service.ts)

    • getSharedCharacter(characterId) - Public endpoint to fetch any character by ID (private sharing)
    • importCharacter(characterId, userId, token) - Creates a copy of shared character
    • Includes validation for:
      • User can't import their own characters
      • User can't import same character twice
  3. Controller Endpoints (backend/src/character/character.controller.ts)

    • GET /character/shared/:characterId - Public endpoint (no auth required) - works with private characters
    • POST /character/import/:characterId - Authenticated endpoint with credit check
    • 10 credits required to import a character
    • Tracks import metadata and increments times_shared counter
    • No publishing required - characters stay private

Mobile App Changes (React Native/Expo)

  1. Deep Linking Configuration

    • Updated mobile/app.json - Changed scheme from "myapp" to "maerchenzauber"
    • Created mobile/app/+native-intent.tsx - Deep link handler
    • Handles URLs: maerchenzauber://share/character/{characterId}
  2. Character Preview Screen (mobile/app/character/preview/[characterId].tsx)

    • Displays shared character details
    • Shows all character images with selector
    • Import button with credit requirement displayed
    • Prevents importing own characters
    • Prevents duplicate imports
  3. Share Button Component (mobile/components/character/ShareCharacterButton.tsx)

    • Simple "Share Character" button
    • Native share dialog integration
    • No publishing required - direct private sharing
    • Generates deep link and opens native share sheet
  4. Integration (mobile/app/character/[id].tsx)

    • Added ShareCharacterButton to character detail screen
    • Positioned below character name/sharing status

Database Schema Requirements

The following fields should exist in the characters table:

-- Existing fields (already in use):
- id (uuid)
- user_id (uuid)
- name (text)
- image_url (text)
- images_data (jsonb)
- user_description (text)
- character_description (text)
- character_description_prompt (text)
- is_animal (boolean)
- animal_type (text)
- blur_hash (text)
-- Publishing fields (optional - not used in private sharing):
- is_published (boolean) -- Can remain, but not required for sharing
- sharing_preference (text) -- Can remain, but not used in this implementation
- share_code (text) -- Can remain, but not used in this implementation
- published_at (timestamp) -- Can remain, but not used in this implementation

-- Existing field (already in database):
- cloned_from (uuid) -- References id of original character (used for imports)

-- New fields added by migration:
- imported_from_user_id (text) -- User ID of original creator
- imported_at (timestamptz) -- When character was imported
- times_shared (integer) -- Counter for how many times character was imported (default: 0)

Migration Applied

The migration has been applied to your database. The following fields were added:

  • imported_from_user_id (text)
  • imported_at (timestamptz)
  • times_shared (integer, default 0)

And we're using the existing cloned_from field to track the original character.

Testing Guide

Setup

  1. Restart Backend: cd backend && npm run dev
  2. Restart Mobile App with Cache Clear: cd mobile && npx expo start -c
    • Important: Use -c flag to clear cache after app.json scheme change

Test Scenario 1: Private Sharing (Simplified)

  1. Login as User A
  2. Navigate to any character you created (doesn't need to be published)
  3. Tap "Share Character" button
    • Native share dialog appears immediately
    • No publishing modal required
  4. Share dialog shows:
    • Share link format: maerchenzauber://share/character/{characterId}
    • Share via any method (Messages, WhatsApp, Email, Copy Link)
    • Send link to yourself or another device for testing

Test Scenario 2: Receiving and Importing

  1. Login as User B (different account)
  2. Open the shared link
    • Tap the deep link you shared
    • App should open to character preview screen
  3. Preview screen should show:
    • Character images (with image selector if multiple)
    • Character name and description
    • Animal type badge (if applicable)
    • "Import to My Library (10 Credits)" button
  4. Tap Import button
    • Confirm dialog should appear
    • Should show "Import for 10 credits?"
    • Tap confirm
  5. After successful import:
    • Success toast appears
    • Navigate to imported character detail
    • Character should be in User B's library

Test Scenario 3: Edge Cases

  1. User tries to import their own character

    • Should show error: "Cannot import your own character"
  2. User tries to import same character twice

    • Should show error: "You have already imported this character"
  3. User with insufficient credits

    • Should show error: "You need 10 credits to import..."
  4. User tries to access deleted character

    • Deep link should show error: "Character not found"

Test deep link manually:

# iOS
xcrun simctl openurl booted "maerchenzauber://share/character/YOUR_CHARACTER_ID"

# Android
adb shell am start -W -a android.intent.action.VIEW -d "maerchenzauber://share/character/YOUR_CHARACTER_ID"

API Endpoints

Get Shared Character (Public)

GET /character/shared/:characterId

Response:

{
  "data": {
    "id": "uuid",
    "name": "Character Name",
    "image_url": "https://...",
    "images_data": [...],
    "user_description": "...",
    "is_animal": true,
    "animal_type": "fox",
    "user_id": "creator-uuid"
  }
}

Import Character

POST /character/import/:characterId
Authorization: Bearer <token>

Response:

{
  "data": {
    "id": "new-uuid",
    "name": "Character Name",
    "imageUrl": "https://...",
    ...
  },
  "message": "Character imported successfully"
}
maerchenzauber://share/character/{characterId}

Example:

maerchenzauber://share/character/a1b2c3d4-e5f6-7890-abcd-ef1234567890

File Changes Summary

New Files

  1. backend/src/character/dto/import-character.dto.ts
  2. mobile/app/+native-intent.tsx
  3. mobile/app/character/preview/[characterId].tsx
  4. mobile/components/character/ShareCharacterButton.tsx
  5. CHARACTER_SHARING_IMPLEMENTATION.md (this file)

Modified Files

  1. backend/src/core/services/supabase-jsonb-auth.service.ts
  2. backend/src/character/character.controller.ts
  3. mobile/app.json
  4. mobile/app/character/[id].tsx

Known Limitations

  1. No web fallback - Deep links only work when app is installed
  2. No share analytics - Not tracking link opens or share sources
  3. No creator rewards - Original creator doesn't receive credits when character is imported
  4. No QR codes - Only URL-based sharing implemented

Future Enhancements

  1. Universal Links - Add web landing page for users without app
  2. Share Analytics - Track how many times links are clicked
  3. Creator Rewards - Give original creator 5 credits when someone imports
  4. QR Code Generation - Allow generating QR codes for in-person sharing
  5. Popular Characters Leaderboard - Show most imported characters
  6. Share Templates - Custom messages for different platforms

Troubleshooting

  • Ensure you restarted Expo with -c flag after changing app.json
  • Check that URL scheme is maerchenzauber (not myapp)
  • On iOS, deep links might not work in simulator for some URLs - test on device
  • On Android, ensure app is in foreground or background (not force-closed)

Import Failing

  • Check user has 10+ credits available
  • Verify character is published (is_published = true)
  • Check character sharing_preference is not 'private'
  • Ensure user is not trying to import their own character

Backend Errors

  • Check Supabase RLS policies allow reading published characters
  • Verify all new database fields exist in characters table
  • Check backend logs for specific error messages

Success Criteria

User can share any private character directly (no publishing required) Share button opens native share dialog immediately Deep link opens app to character preview screen Character preview shows all character details Import button validates credits (10 required) Import creates new character copy in user's library Prevents self-import and duplicate imports Times_shared counter increments on original character Share button integrates seamlessly into character detail screen Character remains private to owner - only people with link can import

Testing Checklist

  • Backend endpoints return correct responses
  • Deep links open app to preview screen
  • Character preview displays all information (even for private characters)
  • Import validates credits correctly
  • Import creates character copy
  • Prevents self-import
  • Prevents duplicate import
  • Share button works without publishing (private sharing)
  • Share dialog shows correct deep link
  • Credit deduction works (10 credits)
  • Toast notifications appear correctly
  • Error handling works for all edge cases
  • Original character remains private after sharing

Implementation Date: 2025-10-31 Status: Complete - Ready for Testing