Files
2026-08-06 11:16:47 +02:00

314 lines
13 KiB
Markdown

# Technical Documentation
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ Expo App Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Tasks │ │ Calendar │ │ Settings │ │
│ │ Screen │ │ Screen │ │ Screen │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ React Hook Form + Zod │ │
│ │ (Form Validation) │ │
│ └─────────────────────────────────────────────┘ │
└────────────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Database Layer │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ WatermelonDB Database │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Categories│ │ Tasks │ │ Subtasks │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ SQLite Adapter (expo-sqlite) │ │
│ └─────────────────────────────────────────────────────────┘ │
└────────────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Sync Layer │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Custom Sync Adapter │ │
│ │ • pullChanges() - GET /sync?since={timestamp} │ │
│ │ • pushChanges() - POST /sync/push │ │
│ │ • Conflict resolution (last-write-wins) │ │
│ └─────────────────────────────────────────────────────────┘ │
└────────────────────────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Backend API │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Custom Postgres API │ │
│ │ • REST or GraphQL endpoints │ │
│ │ • Authentication (JWT/OAuth) │ │
│ │ • Conflict detection & resolution │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Database Schema
### Tables
```sql
-- Categories table
CREATE TABLE categories (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
color TEXT NOT NULL,
"order" INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
-- Tasks table
CREATE TABLE tasks (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
category_id TEXT NOT NULL,
priority TEXT NOT NULL DEFAULT 'none',
completed INTEGER NOT NULL DEFAULT 0,
due_date INTEGER NOT NULL DEFAULT 0,
due_time TEXT NOT NULL DEFAULT '',
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (category_id) REFERENCES categories(id)
);
CREATE INDEX idx_tasks_category ON tasks(category_id);
CREATE INDEX idx_tasks_due_date ON tasks(due_date);
CREATE INDEX idx_tasks_completed ON tasks(completed);
-- Subtasks table
CREATE TABLE subtasks (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL,
title TEXT NOT NULL,
completed INTEGER NOT NULL DEFAULT 0,
"order" INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (task_id) REFERENCES tasks(id)
);
CREATE INDEX idx_subtasks_task ON subtasks(task_id);
```
## Key Components
### DatabaseProvider (`src/hooks/useDatabase.tsx`)
Central context provider managing:
- Database initialization
- Default category seeding
- Collection access
- Ready state
```typescript
interface DatabaseContextType {
database: Database;
collections: {
categories: Collection<Category>;
tasks: Collection<Task>;
subtasks: Collection<Subtask>;
};
isReady: boolean;
initializeDatabase: () => Promise<void>;
}
```
### useTasks Hook (`src/hooks/useTasks.tsx`)
Reactive task queries with loading states:
```typescript
function useTasks(categoryId?: string, showCompleted = false): {
tasks: Task[];
loading: boolean;
}
```
### WatermelonDB Models
All models use decorators for field mapping:
```typescript
@field('column_name') propertyName!: Type;
@date('column_name') dateProperty!: Date;
@children('table_name') relationName!: Query<Model>;
```
## Sync Implementation
### Pull Strategy
1. Client sends `lastPulledAt` timestamp
2. Server returns all changes since timestamp
3. Client applies changes in transaction:
- Upsert categories (by id)
- Upsert tasks (by id)
- Upsert subtasks (by id)
### Push Strategy
1. Client collects local changes (created/updated/deleted)
2. Sends batched changes with `lastPulledAt`
3. Server applies and returns new timestamp
### Conflict Resolution
Default: Last-write-wins based on `updatedAt`
Customizable in `src/database/sync.ts`
## Navigation Structure
```
app/
├── _layout.tsx # Stack navigator + DatabaseProvider
│ ├── (tabs)/ # Tab group
│ │ ├── _layout.tsx # Tabs navigator
│ │ ├── index.tsx # / (Tasks)
│ │ ├── calendar.tsx # /calendar
│ │ └── settings.tsx # /settings
│ └── add-task.tsx # /add-task (modal push)
```
### Navigation Patterns
- **Tabs**: Persistent bottom navigation
- **Add Task**: Push onto stack (modal on iOS)
- **Edit Task**: Push onto stack (future)
## Form Handling
### Add Task Form (`app/add-task.tsx`)
Uses `react-hook-form` with `zod` resolver:
```typescript
const schema = z.object({
title: z.string().min(1).max(100),
description: z.string().max(1000).optional(),
categoryId: z.string().min(1),
priority: z.enum(['none', 'low', 'medium', 'high', 'critical']),
dueDate: z.date().nullable().optional(),
dueTime: z.string().optional(),
subtasks: z.array(z.object({ title: z.string() })).optional(),
});
```
### Field Components
Each field is a controlled component using `Controller`:
- `CategorySelector` - Horizontal chip selection
- `TaskNameInput` - Text input with validation
- `SubtasksSection` - FieldArray for dynamic subtasks
- `DateTimePicker` - Native date/time pickers
- `PrioritySelector` - Segmented control
- `DescriptionInput` - Multiline text area
## Animations
### Reanimated 3 Patterns
1. **FAB Rotation** (`FloatingActionButton.tsx`)
```typescript
const rotateAnim = useSharedValue(0);
rotateAnim.value = (rotateAnim.value + 0.25) % 1;
```
2. **Category Selection** (`CategoryFilter.tsx`)
```typescript
const scaleAnim = useSharedValue(1);
scaleAnim.value = withTiming(selected ? 1.05 : 1);
```
3. **Checkbox Toggle** (`TaskItem.tsx`)
```typescript
const checkedAnim = useSharedValue(0);
checkedAnim.value = withTiming(task.completed ? 1 : 0);
```
## Platform-Specific Considerations
### iOS
- DateTimePicker: `display="spinner"`
- Keyboard: `behavior="padding"`
- Safe areas: Automatic via `react-native-safe-area-context`
### Android
- DateTimePicker: `display="default"`
- Keyboard: `behavior="height"`
- Elevation shadows for Material Design
### Web
- React Native Web for DOM rendering
- DateTimePicker falls back to HTML inputs
- No native SQLite (uses IndexedDB via expo-sqlite web polyfill)
## Performance Optimizations
1. **Lazy Loading**: Components loaded on demand
2. **Memoization**: `React.memo` for list items
3. **Virtualized Lists**: FlatList with `windowSize`
4. **Query Optimization**: Indexed columns for common queries
5. **Batch Writes**: Database transactions for multiple operations
## Testing Strategy
### Unit Tests
- Hooks: `useTasks`, `useCategories`
- Validators: Zod schemas
- Utilities: Date formatting, priority sorting
### Integration Tests
- Database CRUD operations
- Sync adapter with mock server
- Form submission flow
### E2E Tests
- Critical user flows (add task, complete, filter)
- Offline/online transitions
- Cross-platform consistency
## Deployment Checklist
- [ ] Configure `app.json` with production values
- [ ] Set `EXPO_PUBLIC_API_URL` environment variable
- [ ] Enable EAS Build for iOS/Android
- [ ] Configure code signing certificates
- [ ] Set up CI/CD pipeline
- [ ] Test offline sync scenarios
- [ ] Verify push notifications (if enabled)
- [ ] Performance profiling on device
## Troubleshooting
### Common Issues
**Database not initializing**
- Check `expo-sqlite` installation
- Verify `DatabaseProvider` wraps app root
- Check Metro bundler cache: `npx expo start -c`
**Sync failing**
- Verify API endpoint accessibility
- Check CORS headers on server
- Inspect network requests in React Native Debugger
**TypeScript errors with WatermelonDB**
- Ensure `experimentalDecorators: true` in tsconfig
- Check `@nozbe/watermelondb` version compatibility
- Run `npx expo-doctor` for dependency issues
**Animation not working**
- Verify `react-native-reanimated` babel plugin
- Check `useNativeDriver: true` for supported properties
- Ensure worklet functions are properly extracted