This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user