diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..75a2a4d --- /dev/null +++ b/docs/README.md @@ -0,0 +1,72 @@ +# Immich Mobile App Documentation + +This documentation provides a comprehensive guide to the Immich mobile application architecture, features, and implementation details. It is designed to be language-agnostic so developers can recreate the app in any technology stack. + +## Table of Contents + +### Getting Started +- [Architecture Overview](./architecture.md) - High-level architecture and design patterns +- [Project Structure](./structure.md) - Code organization and folder structure + +### Core Features +- [Authentication & Login](./login-flow.md) - Server connection, login, OAuth, and session management +- [Main Navigation](./main-screens.md) - Tab navigation and main screens overview +- [Timeline & Photos](./timeline.md) - Photo timeline, grid display, and asset management +- [Search](./search.md) - Smart search, filters, and discovery features +- [Albums](./albums.md) - Album creation, sharing, and management +- [Library](./library.md) - Library features including favorites, archive, trash, and more + +### Media Features +- [Gallery Viewer](./gallery-viewer.md) - Full-screen image/video viewer +- [Backup & Upload](./backup.md) - Photo/video backup and upload functionality +- [Download](./download.md) - Asset download to device + +### Social Features +- [Sharing](./sharing.md) - Share links and external sharing +- [Partners](./partners.md) - Partner sharing functionality +- [Activities](./activities.md) - Comments and likes on shared albums + +### Background Services +- [Background Services](./background-services.md) - Background backup and sync +- [Real-time Updates](./websocket.md) - WebSocket connection for live updates + +### Technical Reference +- [Data Models](./data-models.md) - Core data structures and models +- [API Reference](./api-reference.md) - Server API endpoints used by the app +- [Local Storage](./local-storage.md) - Database and persistent storage +- [Settings](./settings.md) - App settings and preferences +- [Error Handling](./error-handling.md) - Error messages and handling patterns + +### UI Components +- [UI Components](./ui-components.md) - Reusable UI widgets and components +- [Theming](./theming.md) - Theme system and color schemes + +--- + +## Quick Overview + +Immich is a self-hosted photo and video backup solution. The mobile app provides: + +- **Automatic backup** of photos and videos to your server +- **Timeline view** with chronological photo organization +- **Smart search** using AI-powered image recognition +- **Album management** for organizing photos +- **Sharing** via links or with partners +- **Memories** showing photos from years past +- **Map view** for location-based browsing +- **People recognition** for face-based organization + +## Key Concepts + +| Concept | Description | +|---------|-------------| +| **Asset** | A photo or video, either local (on device) or remote (on server) | +| **Album** | A collection of assets, can be local or remote | +| **Timeline** | Chronological view of all assets | +| **Backup** | Process of uploading local assets to the server | +| **Partner** | Another user who shares their photos with you | +| **Memory** | Photos from the same day in previous years | + +--- + +[Next: Architecture Overview](./architecture.md) diff --git a/docs/albums.md b/docs/albums.md new file mode 100644 index 0000000..681aeda --- /dev/null +++ b/docs/albums.md @@ -0,0 +1,355 @@ +# Albums + +This document describes album creation, management, sharing, and related functionality. + +## Album Overview + +Albums are collections of assets that can be: +- **Personal**: Created by you, only visible to you +- **Shared**: Shared with other users who can view/contribute +- **Local**: Device albums (not synced to server) + +## Album Model + +``` +Album { + id: String // Unique identifier + name: String // Album name + description: String // Album description + ownerId: String // Owner user ID + ownerName: String // Owner display name + createdAt: DateTime // Creation time + updatedAt: DateTime // Last modified + thumbnailAssetId: String? // Cover image asset ID + assetCount: Integer // Number of assets + isShared: Boolean // Has shared users + isActivityEnabled: Boolean // Comments/likes enabled + order: AlbumAssetOrder // asc or desc + sharedUsers: List // Users album is shared with +} +``` + +## Album List Screen + +### Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ Albums [List/Grid] ⚙️ │ +├─────────────────────────────────────────────────────────────┤ +│ [ 🔍 Search albums... ] │ +├─────────────────────────────────────────────────────────────┤ +│ Sort by: Recent Activity ▼ [+ Create Album] │ +├─────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ │ │ │ │ │ │ +│ │ [Cover] │ │ [Cover] │ │ [Cover] │ │ +│ │ │ │ │ │ │ │ +│ │ Summer 2024 │ │ Family │ │ Wedding │ │ +│ │ 24 items │ │ 156 items │ │ 89 items │ │ +│ │ [👤 Shared] │ │ │ │ [👤👤 Shared]│ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Sort Options +| Option | Description | +|--------|-------------| +| Recent Activity | Most recently modified first | +| Album Name | Alphabetical order | +| Most Items | Largest albums first | +| Oldest First | By creation date ascending | + +### Filter Modes +| Mode | Shows | +|------|-------| +| All | All albums | +| My Albums | Albums you own | +| Shared | Albums shared with you | +| Local | On-device albums | + +## Album API Endpoints + +### List Albums +``` +GET /albums?shared=true|false|null + +Response: +[ + { + "id": "album-uuid", + "albumName": "Summer 2024", + "description": "Beach vacation photos", + "ownerId": "user-uuid", + "owner": { user object }, + "albumThumbnailAssetId": "asset-uuid", + "assetCount": 24, + "shared": true, + "albumUsers": [...], + "hasSharedLink": false, + "isActivityEnabled": true, + "order": "desc", + "createdAt": "2024-06-15T10:00:00Z", + "updatedAt": "2024-06-20T15:30:00Z" + } +] +``` + +### Get Album Detail +``` +GET /albums/{id} + +Response: Full album object with assets +``` + +### Create Album +``` +POST /albums + +Body: +{ + "albumName": "New Album", + "description": "Album description", + "assetIds": ["asset-1", "asset-2"], + "albumUsers": [ + { "userId": "user-uuid", "role": "editor" } + ] +} + +Response: Created album object +``` + +### Update Album +``` +PATCH /albums/{id} + +Body: +{ + "albumName": "Updated Name", + "description": "New description", + "isActivityEnabled": true, + "order": "asc" +} +``` + +### Delete Album +``` +DELETE /albums/{id} +``` + +## Album Detail Screen + +### Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Summer 2024 [Select] [⋮] │ +├─────────────────────────────────────────────────────────────┤ +│ Beach vacation with family │ +│ 24 items • Shared with 3 people │ +├─────────────────────────────────────────────────────────────┤ +│ June 2024 │ +│ ┌─────┬─────┬─────┬─────┐ │ +│ │ │ │ │ │ │ +│ │ 📷 │ 📷 │ 🎥 │ 📷 │ │ +│ │ │ │ │ │ │ +│ └─────┴─────┴─────┴─────┘ │ +│ ... │ +├─────────────────────────────────────────────────────────────┤ +│ [+ Add Photos] [💬 Activity] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Album Actions Menu +| Action | Description | +|--------|-------------| +| Edit Album | Change name/description | +| Add Photos | Select assets to add | +| Share | Share with users or create link | +| Album Options | Advanced settings | +| Delete Album | Remove album (owner only) | +| Leave Album | Leave shared album | + +## Album Creation Flow + +### Step 1: Select Assets +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Add to Album [Create] │ +├─────────────────────────────────────────────────────────────┤ +│ Selected: 5 │ +├─────────────────────────────────────────────────────────────┤ +│ [Asset selection grid - same as timeline] │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Step 2: Enter Details +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Create Album │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Album Name │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Summer 2024 │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Description (optional) │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Beach vacation photos │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [Share with users] (optional) │ +│ │ +│ [Create Album] │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Adding Assets to Album + +### API Endpoint +``` +PUT /albums/{id}/assets + +Body: +{ + "ids": ["asset-1", "asset-2", "asset-3"] +} + +Response: +{ + "successfullyAdded": 3, + "alreadyInAlbum": ["asset-4"] +} +``` + +## Removing Assets from Album + +### API Endpoint +``` +DELETE /albums/{id}/assets + +Body: +{ + "ids": ["asset-1", "asset-2"] +} +``` + +## Sharing Albums + +### Share with Users +``` +PUT /albums/{id}/users + +Body: +{ + "albumUsers": [ + { "userId": "user-1", "role": "editor" }, + { "userId": "user-2", "role": "viewer" } + ] +} +``` + +### User Roles +| Role | Can View | Can Add | Can Remove | Can Edit | +|------|----------|---------|------------|----------| +| viewer | ✓ | | | | +| editor | ✓ | ✓ | Own assets | | + +### Remove User from Album +``` +DELETE /albums/{id}/user/{userId} +``` + +### Leave Album (for shared users) +``` +DELETE /albums/{id}/user/me +``` + +## Album Options Screen + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Album Options │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Sort Order │ +│ [Newest first ▼] │ +│ │ +│ ───────────────────────────────────────────────────── │ +│ │ +│ Allow Activity [Toggle: ON] │ +│ Enable comments and likes │ +│ │ +│ ───────────────────────────────────────────────────── │ +│ │ +│ Shared Users │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 John Doe (Editor) [X] │ │ +│ │ 👤 Jane Smith (Viewer) [X] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [+ Add Users] │ +│ │ +│ ───────────────────────────────────────────────────── │ +│ │ +│ [Delete Album] (red, destructive) │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Local Albums + +### Device Albums +- Albums from device photo gallery +- Read-only in the app +- Can be selected for backup +- Synced to server when enabled + +### Local Album Model +``` +LocalAlbum { + id: String // Device album ID + name: String // Album name + assetCount: Integer // Number of assets + modifiedAt: DateTime // Last modified + isAll: Boolean // Is "All Photos" album + thumbnailId: String? // Cover asset ID +} +``` + +## Album Sync + +### Sync Album Feature +When enabled, uploaded assets are automatically added to matching remote albums: + +1. Asset uploaded with album names in metadata +2. Server checks for existing albums with those names +3. Creates albums if they don't exist +4. Adds asset to matching albums + +### API for Album Sync +``` +POST /albums/{albumName}/assets + +Body: +{ + "assetIds": ["asset-1"] +} +``` + +## Album Search + +### Local Search +- Filter album list by name +- Instant results as you type + +### Quick Filter Chips +| Filter | Shows | +|--------|-------| +| All | All albums | +| Mine | Albums you created | +| Shared | Albums shared with you | + +--- + +[Previous: Search](./search.md) | [Next: Library](./library.md) diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..7ebf3a8 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,173 @@ +# Architecture Overview + +This document describes the high-level architecture of the Immich mobile application. + +## Architecture Pattern + +The app follows a **layered architecture** with clear separation of concerns: + +``` +┌─────────────────────────────────────────────────────────┐ +│ UI Layer (Pages/Widgets) │ +├─────────────────────────────────────────────────────────┤ +│ State Management (Providers) │ +├─────────────────────────────────────────────────────────┤ +│ Services (Business Logic) │ +├─────────────────────────────────────────────────────────┤ +│ Repositories (Data Access) │ +├─────────────────────────────────────────────────────────┤ +│ Infrastructure (Database, API, Platform) │ +└─────────────────────────────────────────────────────────┘ +``` + +## Layer Descriptions + +### 1. UI Layer +- **Pages**: Full-screen views/routes (e.g., LoginPage, PhotosPage) +- **Widgets**: Reusable UI components (e.g., AssetGrid, AlbumCard) +- Responsible for rendering and user interaction +- Should contain minimal logic + +### 2. State Management Layer +- Uses reactive state management pattern +- **Providers**: Expose state and actions to the UI +- Handle state changes and notify listeners +- Examples: `authProvider`, `assetProvider`, `albumProvider` + +### 3. Services Layer +- Contains business logic +- Orchestrates operations between repositories +- Examples: + - `AuthService`: Login, logout, session management + - `BackupService`: Photo upload logic + - `AlbumService`: Album CRUD operations + - `SearchService`: Search functionality + +### 4. Repository Layer +- Data access abstraction +- Two types: + - **API Repositories**: Server communication + - **Local Repositories**: Database access +- Examples: `AssetRepository`, `AlbumRepository`, `AuthApiRepository` + +### 5. Infrastructure Layer +- **Database**: Local data persistence (relational database) +- **API Client**: HTTP communication with server +- **Platform APIs**: Native device features (camera, gallery, notifications) + +## Data Flow + +### Read Operation Example (Loading Albums) +``` +UI (AlbumsPage) + │ + ▼ +Provider (albumProvider) + │ + ▼ +Service (AlbumService.refreshRemoteAlbums) + │ + ├──► API Repository (fetch from server) + │ + └──► Local Repository (cache to database) + │ + ▼ +Provider updates state + │ + ▼ +UI rebuilds with new data +``` + +### Write Operation Example (Creating Album) +``` +UI (CreateAlbumPage) + │ + ▼ +Provider (albumProvider.createAlbum) + │ + ▼ +Service (AlbumService.createAlbum) + │ + ├──► API Repository (POST to server) + │ + └──► Local Repository (save to database) + │ + ▼ +Provider updates state + │ + ▼ +UI navigates to new album +``` + +## Key Architectural Decisions + +### 1. Offline-First Approach +- Assets are cached locally in the database +- App works offline with cached data +- Syncs with server when connection is available + +### 2. Dual Asset States +Assets can exist in three states: +- **Local Only**: On device, not uploaded +- **Remote Only**: On server, not downloaded +- **Merged**: Exists both locally and on server + +### 3. Background Processing +- Background backup runs independently +- Uses platform-specific background task APIs +- Maintains separate execution context + +### 4. Reactive Updates +- WebSocket connection for real-time server updates +- Local changes propagate through state management +- UI automatically updates on state changes + +## Component Relationships + +``` +┌──────────────────────────────────────────────────────────────┐ +│ App Entry │ +│ - Initialize databases │ +│ - Set up dependency injection │ +│ - Configure routing │ +└──────────────────────────────────────────────────────────────┘ + │ + ▼ +┌──────────────────────────────────────────────────────────────┐ +│ Router/Navigation │ +│ - Route definitions │ +│ - Auth guards │ +│ - Deep link handling │ +└──────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ Photos │ │ Search │ │ Library │ + │ Tab │ │ Tab │ │ Tab │ + └──────────┘ └──────────┘ └──────────┘ +``` + +## Dependency Injection + +The app uses a dependency injection container to manage: +- Service instances +- Repository instances +- Database connections +- API client configuration + +All dependencies are: +- Lazily initialized +- Scoped appropriately (singleton vs per-use) +- Easily mockable for testing + +## Error Handling Strategy + +1. **API Errors**: Caught at repository level, wrapped in custom exceptions +2. **Service Errors**: Logged and propagated to UI +3. **UI Errors**: Display user-friendly messages via toasts/dialogs +4. **Fatal Errors**: Show error screen with recovery options + +--- + +[Previous: README](./README.md) | [Next: Project Structure](./structure.md) diff --git a/docs/backup.md b/docs/backup.md new file mode 100644 index 0000000..8b3a7b4 --- /dev/null +++ b/docs/backup.md @@ -0,0 +1,339 @@ +# Backup & Upload + +This document describes the photo/video backup functionality, including foreground and background uploads. + +## Backup Overview + +The backup system automatically uploads photos and videos from the device to the server: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Backup Flow │ +├─────────────────────────────────────────────────────────────┤ +│ Device Photos → Backup Service → Server │ +│ │ +│ 1. Select albums to backup │ +│ 2. Find new/changed assets │ +│ 3. Check for duplicates (hash) │ +│ 4. Upload assets │ +│ 5. Update local database │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Backup Configuration + +### Backup Controller Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Backup │ +├─────────────────────────────────────────────────────────────┤ +│ Backup Status │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ ✓ Backup Complete │ │ +│ │ 1,234 of 1,234 backed up │ │ +│ │ Last backup: 2 hours ago │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ Backup Albums │ +│ [Select albums to backup] │ +├─────────────────────────────────────────────────────────────┤ +│ Excluded Albums │ +│ [Select albums to exclude] │ +├─────────────────────────────────────────────────────────────┤ +│ Settings │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Background Backup [Toggle: ON] │ │ +│ │ Use Wi-Fi only [Toggle: ON] │ │ +│ │ Require charging [Toggle: OFF] │ │ +│ │ Ignore iCloud assets [Toggle: OFF] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Album Selection + +### Backup Albums +- Select which device albums to back up +- "Recents" includes all photos +- Individual albums for selective backup + +### Excluded Albums +- Exclude specific albums +- Useful for Screenshots, WhatsApp Images, etc. +- Takes precedence over selected albums + +### Album Selection Logic +``` +Backup Candidates = Selected Albums - Excluded Albums + +If "Recents" selected (Android): + - Back up all device photos + - Individual album folders tracked for sync + +If "Recents" selected (iOS): + - Use "All Photos" album + - Check against excluded albums per asset +``` + +## Backup Process + +### Step 1: Build Upload Candidates +``` +1. Get selected backup albums +2. Get excluded backup albums +3. For each selected album: + a. Check if album modified since last backup + b. Get assets newer than last backup time + c. Add album name to asset metadata +4. Remove assets from excluded albums +``` + +### Step 2: Remove Already Uploaded +``` +1. Check local duplicates database +2. Call server to check existing assets + +POST /assets/exist +Body: +{ + "deviceAssetIds": ["local-id-1", "local-id-2", ...], + "deviceId": "device-uuid" +} + +Response: +{ + "existingIds": ["local-id-1"] +} +``` + +### Step 3: Upload Assets +For each asset: +``` +1. Get original file from device +2. If live photo, also get video component +3. Calculate upload metadata +4. Upload via multipart form + +POST /assets +Content-Type: multipart/form-data + +Fields: + - deviceAssetId: local ID + - deviceId: device UUID + - fileCreatedAt: creation timestamp + - fileModifiedAt: modification timestamp + - isFavorite: boolean + - duration: video duration + +Files: + - assetData: the file + - livePhotoData: motion video (if applicable) +``` + +### Step 4: Handle Response +``` +200 OK: Asset already exists (duplicate) +201 Created: Asset uploaded successfully + +Response: +{ + "id": "remote-asset-uuid", + "deviceAssetId": "local-id", + ... +} +``` + +## Upload Progress Tracking + +### Progress Model +``` +CurrentUploadAsset { + id: String // Local asset ID + fileName: String // File name + fileType: String // IMAGE, VIDEO, etc. + fileSize: Integer // Bytes + fileCreatedAt: DateTime + iCloudAsset: Boolean // Downloading from iCloud +} + +UploadProgress { + bytesUploaded: Integer + totalBytes: Integer + percentage: Double +} +``` + +### Progress UI +``` +┌─────────────────────────────────────────────────────────────┐ +│ Uploading... │ +│ │ +│ IMG_1234.jpg │ +│ [████████████░░░░░░░░░░░░░░░░░] 45% │ +│ │ +│ 12 of 50 • 2.4 MB / 5.1 MB │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Background Backup + +### Platform Differences + +**Android:** +- Uses WorkManager for scheduling +- Triggers on content changes +- Requires battery optimization disabled + +**iOS:** +- Uses Background App Refresh +- Limited execution time +- BGProcessingTask for longer uploads + +### Background Service Configuration +``` +RequireUnmetered: true/false // Wi-Fi only +RequireCharging: true/false // Charging only +TriggerDelay: 5000ms // Minimum delay +MaxDelay: 50000ms // Maximum delay +``` + +### Background Notification +``` +┌─────────────────────────────────────────────────────────────┐ +│ Immich │ +│ Uploading 12/50 photos... │ +│ [Progress bar] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Background Process Flow +``` +1. System triggers background task +2. Acquire lock (prevent concurrent runs) +3. Load translations +4. Initialize database +5. Set API endpoint +6. Get selected/excluded albums +7. Run backup process +8. Update last backup timestamps +9. Release lock +``` + +## iCloud Assets (iOS) + +### Handling iCloud Photos +1. Check if asset is locally available +2. If not, option to download from iCloud +3. Show progress during download +4. Upload after download complete + +### Settings +- `ignoreIcloudAssets`: Skip assets not on device + +## Album Sync Feature + +### Purpose +Automatically organize uploads into server albums matching device album names. + +### How It Works +1. Asset uploaded with album names in metadata +2. After upload, check for matching albums +3. Create album if doesn't exist +4. Add asset to album + +### API +``` +POST /albums/{albumName}/assets +Body: +{ + "assetIds": ["uploaded-asset-id"] +} +``` + +## Duplicate Detection + +### Hash-Based Detection +1. Calculate file hash (checksum) +2. Store in local duplicates database +3. Skip uploads for known duplicates + +### Server-Side Check +``` +POST /assets/exist + +Checks if assets already on server by device ID + local ID +``` + +## Error Handling + +### Retry Logic +- Failed uploads retry automatically +- Exponential backoff +- Maximum retry attempts + +### Error Notifications +``` +┌─────────────────────────────────────────────────────────────┐ +│ ⚠️ Upload Failed │ +│ IMG_5678.jpg could not be uploaded │ +│ Error: Network connection lost │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Common Errors +| Error | Cause | Action | +|-------|-------|--------| +| Network error | No connection | Retry when online | +| Quota exceeded | Storage full | Stop backup, notify user | +| File not found | Asset deleted | Skip asset | +| Permission denied | Gallery access lost | Request permission | + +## Failed Uploads Screen + +### Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Failed Uploads │ +├─────────────────────────────────────────────────────────────┤ +│ 5 uploads failed │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 📷 IMG_1234.jpg │ │ +│ │ Network timeout │ │ +│ │ [Retry] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ ... │ +├─────────────────────────────────────────────────────────────┤ +│ [Retry All] │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Backup Statistics + +### Tracked Metrics +- Total assets to backup +- Successfully uploaded +- Failed uploads +- Duplicate (already on server) +- Last backup timestamp + +### Storage Usage +``` +GET /users/me + +Response includes: +{ + "quotaUsageInBytes": 1234567890, + "quotaSizeInBytes": 10737418240 +} +``` + +## Cellular Data Settings + +### Options +- `useCellularForUploadPhotos`: Allow photo upload on cellular +- `useCellularForUploadVideos`: Allow video upload on cellular + +--- + +[Previous: Gallery Viewer](./gallery-viewer.md) | [Next: Download](./download.md) diff --git a/docs/download.md b/docs/download.md new file mode 100644 index 0000000..0d77293 --- /dev/null +++ b/docs/download.md @@ -0,0 +1,214 @@ +# Download + +This document describes the asset download functionality. + +## Download Overview + +Users can download assets from the server to their device: +- Single asset download +- Bulk download +- Live photo handling +- Video download + +## Download Flow + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Download Flow │ +├─────────────────────────────────────────────────────────────┤ +│ 1. User initiates download │ +│ 2. Create download task │ +│ 3. Download file to temp directory │ +│ 4. Save to device gallery │ +│ 5. Clean up temp file │ +│ 6. Notify completion │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Download API + +### Get Original Asset +``` +GET /assets/{id}/original + +Headers: + Authorization: Bearer {token} + +Response: Binary file data +``` + +### Download Endpoint Pattern +``` +{server_endpoint}/assets/{asset_id}/original +``` + +## Download Task Configuration + +### Task Parameters +``` +DownloadTask { + taskId: String // Usually asset remote ID + url: String // Download URL + headers: Map // Auth headers + filename: String // Target filename + group: String // Task group for tracking + updates: Boolean // Send progress updates + metaData: String? // Additional metadata (JSON) +} +``` + +### Task Groups +| Group | Purpose | +|-------|---------| +| group_image | Image downloads | +| group_video | Video downloads | +| group_livephoto | Live photo downloads | + +## Download Types + +### Image Download +1. Create download task +2. Download to temp directory +3. Save to device gallery (DCIM/Immich) +4. Delete temp file + +### Video Download +Same as image, but: +- Larger file sizes +- Longer download times +- Progress tracking more important + +### Live Photo Download (iOS) +1. Download image component +2. Download video component +3. Combine as live photo +4. Save to gallery + +``` +Download Task 1: Image (original HEIC/JPG) + metadata: { part: "image", id: "asset-id" } + +Download Task 2: Video (motion MOV) + metadata: { part: "video", id: "asset-id" } + +When both complete → Combine and save +``` + +## Download Progress + +### Progress Tracking +``` +┌─────────────────────────────────────────────────────────────┐ +│ Downloads X │ +├─────────────────────────────────────────────────────────────┤ +│ IMG_1234.jpg │ +│ [████████████████░░░░░░░░░░░░░] 65% │ +│ 2.4 MB / 3.7 MB │ +├─────────────────────────────────────────────────────────────┤ +│ VID_5678.mp4 │ +│ [████░░░░░░░░░░░░░░░░░░░░░░░░░] 12% │ +│ 15.2 MB / 128.4 MB │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Progress Callbacks +``` +onProgress(bytesReceived, totalBytes) +onStatusChange(taskId, status) +onComplete(taskId, filePath) +onError(taskId, error) +``` + +## Bulk Download + +### Multiple Asset Download +``` +1. Create download task for each asset +2. Queue all tasks +3. Download in parallel (max 6 concurrent) +4. Track overall progress +5. Report failures individually +``` + +### Parallel Download Limits +- Max concurrent: 6 +- Max per host: 6 +- Max per group: 3 + +## Save Locations + +### Android +``` +DCIM/Immich/ + ├── IMG_1234.jpg + ├── VID_5678.mp4 + └── ... +``` + +### iOS +- Saved to Photos app +- In "Immich" album (if created) +- Respects photo permissions + +## Cancel Download + +### Cancel Single +``` +Cancel task by taskId +``` + +### Cancel All +``` +Cancel all tasks in download queue +``` + +## Error Handling + +### Common Errors +| Error | Description | Action | +|-------|-------------|--------| +| Network error | Connection lost | Retry | +| Storage full | No space on device | Alert user | +| File not found | Asset deleted on server | Skip | +| Permission denied | Gallery access denied | Request permission | + +### Retry Logic +- Automatic retry on network errors +- Exponential backoff +- Max 3 retries + +## Download Notification + +### Progress Notification +``` +┌─────────────────────────────────────────────────────────────┐ +│ Immich │ +│ Downloading 3 items... │ +│ [Progress bar] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Completion Notification +``` +┌─────────────────────────────────────────────────────────────┐ +│ Immich │ +│ Download complete │ +│ 3 items saved to gallery │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Large File Handling + +### Android Large Files +- Files > 256MB run in foreground service +- Shows persistent notification +- Prevents system kill + +### Background Download +- Can continue when app backgrounded +- System manages task lifecycle +- Resumes on network recovery + +--- + +[Previous: Backup & Upload](./backup.md) | [Next: Sharing](./sharing.md) diff --git a/docs/gallery-viewer.md b/docs/gallery-viewer.md new file mode 100644 index 0000000..4a34b03 --- /dev/null +++ b/docs/gallery-viewer.md @@ -0,0 +1,293 @@ +# Gallery Viewer + +This document describes the full-screen asset viewer for viewing photos and videos. + +## Overview + +The gallery viewer provides: +- Full-screen photo/video viewing +- Swipe navigation between assets +- Pinch-to-zoom on images +- Video playback controls +- Asset actions and info + +## Viewer Layout + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← [Actions: Share, Edit, Cast, ⋮] Info ℹ️ │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ │ +│ │ +│ [Full Screen Asset] │ +│ │ +│ │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ [Stack thumbnails if stacked] │ +├─────────────────────────────────────────────────────────────┤ +│ ♥ 📤 ⬇️ 🗑 [Add to Album] │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Navigation + +### Gestures +| Gesture | Action | +|---------|--------| +| Swipe left/right | Navigate to next/previous asset | +| Swipe down | Close viewer | +| Swipe up | Show asset info panel | +| Single tap | Toggle controls visibility | +| Double tap | Toggle zoom | +| Pinch | Zoom in/out | + +### Tap-to-Navigate Mode +Optional setting that enables: +- Tap left quarter: Previous asset +- Tap right quarter: Next asset +- Tap center: Toggle controls + +## Image Viewing + +### Loading Strategy +1. Show thumbhash placeholder immediately +2. Load thumbnail from cache +3. Load preview resolution +4. Load original when zoomed + +### Zoom Features +- Pinch to zoom (up to 3x) +- Double tap to toggle zoom +- Pan when zoomed +- Reset on page change + +### Motion Photos +- Long press to play motion component +- Auto-plays on image tap (optional) +- Shows motion indicator icon + +## Video Playback + +### Video Controls +``` +┌─────────────────────────────────────────────────────────────┐ +│ │ +│ [Video Frame] │ +│ │ +│ ▶ / ⏸ │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ 0:45 ────────●────────────────────────── 3:24 │ +│ │ +│ [Mute] [Quality] [Fullscreen] [Cast] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Video Settings +| Setting | Description | +|---------|-------------| +| Auto-play | Start playing when opened | +| Loop | Repeat when finished | +| Load Original | Use full quality (uses more data) | + +### Video Quality +- Transcoded versions available +- Original quality option +- Adaptive based on network + +## Top App Bar + +### Actions Available +| Icon | Action | +|------|--------| +| ← | Close viewer | +| Share | System share sheet | +| Edit | Open image editor | +| Cast | Cast to Chromecast | +| ⋮ | More options menu | +| ℹ️ | Show info panel | + +### More Options Menu +- Download +- Set as album cover +- View in map +- Show motion photo +- Stack/unstack +- Advanced info (debug) + +## Bottom Action Bar + +### Primary Actions +| Icon | Action | +|------|--------| +| ♥ | Toggle favorite | +| 📤 | Share | +| ⬇️ | Download to device | +| 🗑 | Move to trash | +| 📁 | Add to album | + +## Asset Info Panel + +Swipe up or tap info button to show: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ───── (drag handle) │ +├─────────────────────────────────────────────────────────────┤ +│ IMG_1234.jpg │ +│ December 15, 2024 at 3:45 PM │ +├─────────────────────────────────────────────────────────────┤ +│ Details │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 📷 Apple iPhone 15 Pro │ │ +│ │ 📐 4032 × 3024 │ │ +│ │ 💾 4.2 MB │ │ +│ │ 📍 San Francisco, CA │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ EXIF Information │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Aperture: f/1.8 │ │ +│ │ Shutter: 1/120s │ │ +│ │ ISO: 64 │ │ +│ │ Focal Length: 24mm │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ Description │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Add a description... │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ People │ +│ [Face thumbnails with names] │ +├─────────────────────────────────────────────────────────────┤ +│ Location │ +│ [Small map preview] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Info Sections +1. **Header**: Filename and date +2. **Details**: Camera, dimensions, size, location +3. **EXIF**: Technical camera data +4. **Description**: Editable caption +5. **People**: Recognized faces +6. **Location**: Map preview (if geotagged) + +## Asset Stacks + +### What are Stacks? +Groups of similar assets shown as one item: +- Burst photos +- Edited versions +- Similar shots + +### Stack Display +``` +┌─────────────────────────────────────────────────────────────┐ +│ [Main stack asset displayed] │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ +│ │ 1 │ │ 2 │ │ 3 │ │ 4 │ ← Stack thumbnails │ +│ │ ● │ │ │ │ │ │ │ │ +│ └─────┘ └─────┘ └─────┘ └─────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Stack Actions +- Tap thumbnail to switch +- Long press to set as primary +- Unstack to separate assets + +## Editing + +### Available Edits +- Crop and rotate +- Filters +- Adjustments (brightness, contrast, etc.) + +### Edit Flow +1. Tap edit icon +2. Make changes +3. Save as new or overwrite + +## Casting + +### Chromecast Support +- Cast images to TV +- Shows high-resolution version +- Video streaming support + +### Cast Flow +1. Tap cast icon +2. Select device +3. Asset displays on TV +4. Control from phone + +## Download + +### Download Options +- Save to device gallery +- Choose quality (original/transcoded) +- Live photos download both parts + +### Download API +``` +GET /assets/{id}/original + +Response: Binary file data +``` + +## Activities (Comments) + +For shared albums with activity enabled: + +### Activity Panel +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Activity │ +├─────────────────────────────────────────────────────────────┤ +│ 👤 Jane: Love this photo! ♥ │ +│ 👤 John: Great shot! │ +│ 👤 You: Thanks! │ +├─────────────────────────────────────────────────────────────┤ +│ [Add a comment...] [Send] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Activity API +``` +GET /activities?albumId={albumId}&assetId={assetId} + +POST /activities +Body: +{ + "albumId": "album-uuid", + "assetId": "asset-uuid", + "type": "comment" | "like", + "comment": "Great photo!" +} +``` + +## Performance + +### Image Caching +- Thumbnails cached aggressively +- Preview images cached with limits +- Original images not cached (too large) + +### Preloading +- Next/previous images preloaded +- Smooth navigation experience +- Configurable cache size + +### Memory Management +- Release memory when leaving +- Dispose video players +- Clear caches when low memory + +--- + +[Previous: Library](./library.md) | [Next: Backup & Upload](./backup.md) diff --git a/docs/library.md b/docs/library.md new file mode 100644 index 0000000..bbb7188 --- /dev/null +++ b/docs/library.md @@ -0,0 +1,422 @@ +# Library + +This document describes the Library tab features including favorites, archive, trash, and other collections. + +## Library Overview + +The Library tab provides access to various collections and utility features: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Library Features │ +├─────────────────────────────────────────────────────────────┤ +│ Quick Access │ Collections │ Utilities │ +├──────────────────┼──────────────────┼───────────────────────┤ +│ • Favorites │ • People │ • Folders │ +│ • Archive │ • Places │ • Locked Folder │ +│ • Shared Links │ • Local Albums │ • Partners │ +│ • Trash │ │ │ +└──────────────────┴──────────────────┴───────────────────────┘ +``` + +## Favorites + +### Purpose +- Quick access to favorite photos/videos +- Marked assets appear here +- Heart icon on assets + +### Toggle Favorite API +``` +PUT /assets + +Body: +{ + "ids": ["asset-1", "asset-2"], + "isFavorite": true +} +``` + +### UI Features +- Grid view of favorited assets +- Multi-select available +- Can unfavorite from here + +## Archive + +### Purpose +- Hide assets from main timeline +- Assets still searchable +- Good for screenshots, receipts, etc. + +### Toggle Archive API +``` +PUT /assets + +Body: +{ + "ids": ["asset-1", "asset-2"], + "isArchived": true +} +``` + +### Restore from Archive +Same API with `isArchived: false` + +## Trash + +### Purpose +- Deleted assets go here first +- Auto-deleted after 30 days (configurable) +- Can restore or permanently delete + +### Trash Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Trash [Empty All] │ +├─────────────────────────────────────────────────────────────┤ +│ Items in trash will be permanently deleted after 30 days │ +├─────────────────────────────────────────────────────────────┤ +│ [Asset grid of trashed items] │ +│ │ +│ Select to restore or delete permanently │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Trash API Endpoints + +**Move to Trash** +``` +POST /trash/assets + +Body: +{ + "ids": ["asset-1", "asset-2"] +} +``` + +**Restore from Trash** +``` +POST /trash/restore/assets + +Body: +{ + "ids": ["asset-1", "asset-2"] +} +``` + +**Restore All** +``` +POST /trash/restore +``` + +**Empty Trash** +``` +POST /trash/empty +``` + +## Shared Links + +### Purpose +- Create public links to share assets/albums +- Password protection optional +- Expiration dates supported + +### Shared Links Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Shared Links [+ Create] │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 📷 5 items │ │ +│ │ Created: Dec 15, 2024 │ │ +│ │ Expires: Never │ │ +│ │ 🔗 Copy Link [Edit] [X] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 📁 Summer 2024 Album │ │ +│ │ Created: Dec 10, 2024 │ │ +│ │ Expires: Dec 31, 2024 🔒 Password │ │ +│ │ 🔗 Copy Link [Edit] [X] │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Shared Link Model +``` +SharedLink { + id: String + description: String? + slug: String? // Custom URL slug + password: String? // If protected + expiresAt: DateTime? // Expiration + allowDownload: Boolean // Can download + allowUpload: Boolean // Can upload (albums) + showMetadata: Boolean // Show EXIF data + type: LinkType // ALBUM or INDIVIDUAL + albumId: String? // If album link + assets: List // Linked assets + createdAt: DateTime +} +``` + +### Shared Link API + +**Create Link** +``` +POST /shared-links + +Body: +{ + "type": "ALBUM" | "INDIVIDUAL", + "albumId": "album-uuid", // for album links + "assetIds": ["asset-1"], // for individual links + "description": "My photos", + "password": "optional", + "expiresAt": "2024-12-31T23:59:59Z", + "allowDownload": true, + "allowUpload": false, + "showMetadata": true +} +``` + +**Update Link** +``` +PATCH /shared-links/{id} + +Body: (same fields as create) +``` + +**Delete Link** +``` +DELETE /shared-links/{id} +``` + +## People Collection + +### Purpose +- Browse photos by recognized faces +- Name and manage people +- Merge duplicate people + +### People API +``` +GET /people + +Response: +[ + { + "id": "person-uuid", + "name": "John Doe", + "birthDate": "1990-01-15", + "thumbnailPath": "/path", + "isHidden": false + } +] +``` + +### Person Detail Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← John Doe [⋮] │ +├─────────────────────────────────────────────────────────────┤ +│ [Large Face Thumbnail] │ +│ │ +│ 156 photos │ +├─────────────────────────────────────────────────────────────┤ +│ [Grid of assets featuring this person] │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Update Person +``` +PUT /people/{id} + +Body: +{ + "name": "John Doe", + "birthDate": "1990-01-15", + "isHidden": false +} +``` + +## Places Collection + +### Purpose +- Browse photos by location +- Map view of all geotagged photos +- Location clustering + +### Places Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Places │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ │ │ +│ │ [Interactive Map] │ │ +│ │ with asset markers │ │ +│ │ │ │ +│ └─────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ [Grid of cities/places with thumbnails] │ +│ │ +│ San Francisco New York Paris │ +│ 24 photos 56 photos 12 photos │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Map Features +- Tap marker to see asset +- Cluster markers for dense areas +- Filter by date, favorites, partners +- Different map themes (light/dark) + +## Local Albums (On This Device) + +### Purpose +- Access device photo albums +- Select albums for backup +- View local-only content + +### Local Album Features +- Shows all device photo albums +- Displays asset count +- Can browse contents +- Cannot modify from app (read-only) + +## Folders + +### Purpose +- Browse by server folder structure +- See how photos are organized on server +- Navigate folder hierarchy + +### Folder API +``` +GET /view/folder?path=/ + +Response: +{ + "folders": ["2024", "2023", "Screenshots"], + "assets": [...] +} +``` + +## Locked Folder + +### Purpose +- PIN-protected private folder +- Hidden from main views +- Biometric unlock option + +### Locked Folder Flow +``` +┌─────────────────────────────────────────────────────────────┐ +│ Locked Folder │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ 🔒 │ +│ │ +│ Enter PIN to unlock │ +│ │ +│ ┌───┬───┬───┬───┐ │ +│ │ ● │ ● │ ○ │ ○ │ │ +│ └───┴───┴───┴───┘ │ +│ │ +│ [Use Biometrics] │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Setup PIN +``` +PUT /auth/pin-code + +Body: +{ + "pinCode": "1234" +} +``` + +### Verify PIN +``` +POST /auth/pin-code/verify + +Body: +{ + "pinCode": "1234" +} +``` + +### Locked Assets +Assets can be moved to/from locked folder: +``` +PUT /assets + +Body: +{ + "ids": ["asset-1"], + "visibility": "locked" | "timeline" +} +``` + +## Partners + +### Purpose +- View photos shared by partners +- Include partner photos in timeline +- Manage partner relationships + +### Partners Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Partners │ +├─────────────────────────────────────────────────────────────┤ +│ Sharing with you: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 Jane Doe │ │ +│ │ Include in timeline: [Toggle: ON] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ You're sharing with: │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 John Smith [Remove] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [+ Add Partner] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Partner API + +**Get Partners** +``` +GET /partners?direction=shared-with|shared-by +``` + +**Add Partner** +``` +POST /partners/{userId} +``` + +**Remove Partner** +``` +DELETE /partners/{userId} +``` + +**Update Partner Settings** +``` +PUT /partners/{userId} + +Body: +{ + "inTimeline": true +} +``` + +--- + +[Previous: Albums](./albums.md) | [Next: Gallery Viewer](./gallery-viewer.md) diff --git a/docs/login-flow.md b/docs/login-flow.md new file mode 100644 index 0000000..18550af --- /dev/null +++ b/docs/login-flow.md @@ -0,0 +1,362 @@ +# Authentication & Login + +This document describes the authentication flow, login process, and session management. + +## Overview + +The app supports two authentication methods: +1. **Email/Password Login** - Traditional credentials +2. **OAuth Login** - External identity provider (SSO) + +## Login Flow Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Splash Screen │ +│ - Check for existing session │ +│ - Initialize databases │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ Has Valid Token │ │ No Token/ │ + │ │ │ Token Expired │ + └─────────────────┘ └─────────────────┘ + │ │ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ Main App │ │ Login Page │ + │ (Timeline) │ │ │ + └─────────────────┘ └─────────────────┘ +``` + +## Login Page UI + +### Screen Layout +``` +┌─────────────────────────────────────┐ +│ │ +│ [Immich Logo] │ +│ (animated) │ +│ │ +│ "immich" text │ +│ │ +├─────────────────────────────────────┤ +│ Server URL │ +│ ┌─────────────────────────────┐ │ +│ │ https://your-server.com │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [Next Button] │ +│ │ +│ [Settings] │ +│ │ +├─────────────────────────────────────┤ +│ v1.2.3 Logs │ +└─────────────────────────────────────┘ +``` + +After server validation: +``` +┌─────────────────────────────────────┐ +│ │ +│ [Immich Logo] │ +│ │ +│ server-url.com │ +│ │ +├─────────────────────────────────────┤ +│ Email │ +│ ┌─────────────────────────────┐ │ +│ │ user@example.com │ │ +│ └─────────────────────────────┘ │ +│ │ +│ Password │ +│ ┌─────────────────────────────┐ │ +│ │ •••••••• │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [Login Button] │ +│ ───────────── │ +│ [OAuth Button] │ +│ │ +│ [Back] │ +└─────────────────────────────────────┘ +``` + +## Server URL Validation + +### Process +1. User enters server URL +2. App sanitizes URL (adds https:// if missing, removes trailing slash) +3. Attempts to resolve endpoint via `/.well-known/immich` +4. Falls back to `{url}/api` if well-known not found +5. Pings server to verify reachability +6. Stores validated URL locally + +### Well-Known Discovery +``` +GET {server_url}/.well-known/immich + +Response: +{ + "api": { + "endpoint": "/api" // or absolute URL + } +} +``` + +### Server Info Fetch +After URL validation, fetch server configuration: +``` +GET /server/config +GET /server/features +``` + +This determines: +- Whether OAuth is enabled +- Whether password login is enabled +- OAuth button text +- Server version (for compatibility check) + +## Email/Password Login + +### API Request +``` +POST /auth/login + +Headers: + deviceModel: "iPhone 14 Pro" | "Pixel 7" + deviceType: "iOS" | "Android" + +Body: +{ + "email": "user@example.com", + "password": "secret123" +} + +Response: +{ + "accessToken": "eyJhbG...", + "userId": "uuid-here", + "userEmail": "user@example.com", + "name": "John Doe", + "isAdmin": false, + "shouldChangePassword": false, + "profileImagePath": "/path/to/image" +} +``` + +### Post-Login Actions +1. Store access token securely +2. Store user info locally +3. Generate/retrieve device ID +4. Connect WebSocket for real-time updates +5. Request gallery permissions +6. Start initial sync +7. Navigate to main app + +## OAuth Login (PKCE Flow) + +### Flow Diagram +``` +┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ +│ App │ │ Server │ │ OAuth │ │ App │ +│ │ │ │ │ Provider │ │ │ +└────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ + │ │ │ │ + │ 1. Request │ │ │ + │ OAuth URL │ │ │ + │───────────────►│ │ │ + │ │ │ │ + │ 2. Return URL │ │ │ + │ + state │ │ │ + │◄───────────────│ │ │ + │ │ │ │ + │ 3. Open Browser with code_challenge │ + │────────────────────────────────►│ │ + │ │ │ │ + │ 4. User authenticates │ │ + │ │ │ │ + │ 5. Redirect with auth code │ │ + │◄────────────────────────────────│ │ + │ │ │ │ + │ 6. Exchange code for token (with code_verifier) │ + │───────────────►│ │ │ + │ │ │ │ + │ 7. Return access token │ │ + │◄───────────────│ │ │ +``` + +### PKCE Parameters +- **code_verifier**: Random 43-128 character string +- **code_challenge**: SHA256 hash of verifier, base64url encoded +- **state**: Random 32 character string for CSRF protection + +### OAuth Request +``` +GET /oauth/authorize? + code_challenge={code_challenge}& + code_challenge_method=S256& + state={state} + +Response: +{ + "url": "https://oauth-provider.com/auth?client_id=..." +} +``` + +### OAuth Callback +``` +POST /oauth/callback + +Body: +{ + "url": "immich://oauth-callback?code=xxx&state=yyy", + "codeVerifier": "original_code_verifier" +} + +Response: +{ + "accessToken": "eyJhbG...", + "userId": "uuid-here", + ... +} +``` + +## Session Management + +### Access Token Storage +- Stored in secure/encrypted storage +- Token included in all API requests via header +- Automatically loaded on app start + +### Request Authentication +All authenticated requests include: +``` +Headers: + Authorization: Bearer {access_token} + x-immich-user-token: {access_token} // alternative +``` + +### Token Validation +On app start: +1. Load stored token +2. Call `GET /users/me` to validate +3. If valid, proceed to app +4. If invalid (401), redirect to login + +## Logout + +### Process +1. Call server logout endpoint +2. Clear local access token +3. Clear user data from database +4. Disable background backup +5. Cancel ongoing sync operations +6. Navigate to login screen + +### API Request +``` +POST /auth/logout +``` + +### Data Cleared on Logout +- Access token +- Current user info +- Asset ETag (sync marker) +- Auto endpoint switching settings +- Preferred WiFi name +- Local/external endpoint list + +## Password Change + +If `shouldChangePassword` is true after login: +1. Redirect to Change Password screen +2. User enters new password +3. Submit password change + +``` +PUT /auth/change-password + +Body: +{ + "password": "newSecurePassword123" +} +``` + +## Multi-Server Support + +### Automatic Endpoint Switching +The app can automatically switch between local and external server URLs based on WiFi network: + +1. Configure local endpoint (e.g., `http://192.168.1.100:2283`) +2. Configure external endpoint (e.g., `https://photos.example.com`) +3. Set preferred WiFi network name +4. When on preferred WiFi, use local endpoint +5. Otherwise, use external endpoint + +### Auxiliary Endpoint Validation +Before switching, validate the new endpoint is reachable: +``` +GET {endpoint}/users/me + +Response: 200 OK = valid endpoint +``` + +## PIN Code / Local Auth + +For the locked folder feature: +- User can set up a PIN code +- PIN is stored securely on device +- Biometric authentication can be used as alternative + +### PIN Setup +``` +PUT /auth/pin-code + +Body: +{ + "pinCode": "1234" +} +``` + +### PIN Verification +``` +POST /auth/pin-code/verify + +Body: +{ + "pinCode": "1234" +} +``` + +## Error Handling + +### Common Login Errors + +| Error | User Message | Cause | +|-------|--------------|-------| +| Invalid URL | "Invalid server URL" | Malformed URL entered | +| Server unreachable | "Server is not reachable" | Network issue or wrong URL | +| SSL Error | "SSL certificate error" | Self-signed cert not trusted | +| Invalid credentials | "Login failed" | Wrong email/password | +| OAuth failed | "OAuth login failed" | OAuth flow interrupted | +| API exception | Error message from server | Server-side error | + +### Validation Rules +- **URL**: Must be valid URL with http/https scheme +- **Email**: Must contain @, no leading/trailing spaces +- **Password**: No specific validation (server handles it) + +## Version Compatibility + +After connecting to server, check version compatibility: +1. Get app version +2. Get server version from `/server/version` +3. Compare major/minor versions +4. Show warning if mismatch detected + +--- + +[Previous: Project Structure](./structure.md) | [Next: Main Navigation](./main-screens.md) diff --git a/docs/main-screens.md b/docs/main-screens.md new file mode 100644 index 0000000..76cdf78 --- /dev/null +++ b/docs/main-screens.md @@ -0,0 +1,262 @@ +# Main Navigation + +This document describes the main app navigation structure and the primary screens. + +## Tab Navigation + +The app uses a bottom tab bar with 4 main sections: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ │ +│ Current Tab Content │ +│ │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ Photos │ Search │ Albums │ Library │ +│ 📷 │ 🔍 │ 📁 │ 📚 │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Tab Indices +| Index | Tab | Description | +|-------|-----|-------------| +| 0 | Photos | Main timeline view | +| 1 | Search | Smart search and explore | +| 2 | Albums | Album management | +| 3 | Library | Collections and utilities | + +## Photos Tab (Timeline) + +The primary view showing all photos and videos chronologically. + +### Screen Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ App Bar [Avatar] ⚙️ │ +├─────────────────────────────────────────────────────────────┤ +│ ┌───────────────────────────────────────────────────────┐ │ +│ │ Memory Lane (Horizontal Scroll) │ │ +│ │ [1 Year] [2 Years] [3 Years] [4 Years] │ │ +│ └───────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ December 2024 │ +│ ┌─────┬─────┬─────┬─────┐ │ +│ │ │ │ │ │ │ +│ │ 📷 │ 📷 │ 🎥 │ 📷 │ │ +│ │ │ │ │ │ │ +│ ├─────┼─────┼─────┼─────┤ │ +│ │ │ │ │ │ │ +│ │ 📷 │ 📷 │ 📷 │ 🎥 │ │ +│ │ │ │ │ │ │ +│ └─────┴─────┴─────┴─────┘ │ +│ November 2024 │ +│ ... │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Features +- **Memory Lane**: Shows photos from years past +- **Grouped by date**: Sections for each month/day +- **Pull to refresh**: Sync with server +- **Multi-select**: Long press to select multiple assets +- **Scroll scrubber**: Quick navigation by date + +### App Bar Components +- **User avatar**: Tap to access user menu +- **Settings icon**: Navigate to settings +- **Shows backup status**: Indicator when backup active + +### Memory Lane +- Horizontal scrollable cards +- Each card represents photos from X years ago +- Tapping opens memory viewer +- Only shows if user has memories enabled + +## Search Tab + +Smart search with filters and discovery features. + +### Screen Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Back [ Search photos and videos... ] [Menu] │ +├─────────────────────────────────────────────────────────────┤ +│ [People] [Location] [Camera] [Date] [Type] [Display] │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Search Results Grid │ +│ OR │ +│ Empty State with │ +│ Quick Links: │ +│ - Recently Taken │ +│ - Videos │ +│ - Favorites │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Search Types +| Type | Description | Example | +|------|-------------|---------| +| Context | AI-powered semantic search | "sunset on beach" | +| Filename | Search by filename | "IMG_2024" | +| Description | Search asset descriptions | "birthday party" | +| OCR | Search text in images | "receipt" | + +### Filter Chips +- **People**: Filter by recognized faces +- **Location**: Filter by country/state/city +- **Camera**: Filter by camera make/model +- **Date**: Date range picker +- **Media Type**: Images, Videos, or All +- **Display Options**: Favorites, Archived, Not in Album + +## Albums Tab + +View and manage albums. + +### Screen Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ Albums [Grid] │ +├─────────────────────────────────────────────────────────────┤ +│ [Search albums...] │ +├─────────────────────────────────────────────────────────────┤ +│ Sort: Recent ▼ [+ Create Album] │ +├─────────────────────────────────────────────────────────────┤ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ │ │ │ │ │ │ +│ │ [Thumb] │ │ [Thumb] │ │ [Thumb] │ │ +│ │ │ │ │ │ │ │ +│ │ Album 1 │ │ Album 2 │ │ Album 3 │ │ +│ │ 24 items │ │ 12 items │ │ 8 items │ │ +│ │ [Shared] │ │ │ │ │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ │ +│ │ │ │ │ │ +│ │ [Thumb] │ │ [Thumb] │ │ +│ │ │ │ │ │ +│ │ Album 4 │ │ Album 5 │ │ +│ │ 156 items │ │ 3 items │ │ +│ └──────────────┘ └──────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Album Types +- **My Albums**: Albums you created +- **Shared Albums**: Albums shared with you +- **Local Albums**: Albums on device (not synced) + +### Sort Options +- Recently Activity +- Album Name +- Most Items +- Last Modified + +### Album Card Shows +- Thumbnail image +- Album name +- Item count +- Shared indicator (if applicable) +- Owner avatar (if shared) + +## Library Tab + +Collection of features and utilities. + +### Screen Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ App Bar │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ ♥ Favorites │ │ 📦 Archive │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ 🔗 Shared Links │ │ 🗑 Trash │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ [People Grid] [Places Map] │ │ +│ │ 4 face thumbnails Map preview │ │ +│ │ │ │ +│ │ People Places │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ [Local Albums Grid] │ │ +│ │ 4 album thumbnails │ │ +│ │ │ │ +│ │ On This Device │ │ +│ └────────────────────────────────────────────────────────┘ │ +├─────────────────────────────────────────────────────────────┤ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ 📁 Folders │ │ +│ │ 🔒 Locked Folder │ │ +│ │ 👥 Partners │ │ +│ │ └─ Partner 1's photos │ │ +│ │ └─ Partner 2's photos │ │ +│ └────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Quick Access Buttons +| Button | Description | +|--------|-------------| +| Favorites | View all favorited assets | +| Archive | View archived assets (hidden from timeline) | +| Shared Links | Manage public sharing links | +| Trash | View deleted items (before permanent deletion) | + +### Collection Cards +- **People**: Face recognition results, shows top 4 faces +- **Places**: Map thumbnail, opens map view +- **On This Device**: Local albums on device + +### Additional Features +- **Folders**: Browse by folder structure +- **Locked Folder**: PIN-protected private photos +- **Partners**: View photos shared by partners + +## Navigation Guards + +Routes are protected by guards that check: + +| Guard | Purpose | +|-------|---------| +| AuthGuard | Requires valid authentication | +| DuplicateGuard | Prevents duplicate route pushes | +| BackupPermissionGuard | Requires gallery permission for backup | +| GalleryGuard | Checks gallery access | +| LockedGuard | Requires PIN for locked folder | + +## Deep Linking + +The app supports deep links for: +- `immich://` scheme for OAuth callbacks +- `my.immich.app` host for web-to-app links +- Specific routes like shared albums, memories + +### Deep Link Examples +``` +immich://oauth-callback?code=xxx&state=yyy +https://my.immich.app/share/xxx +https://my.immich.app/albums/xxx +``` + +## App Lifecycle + +The app responds to lifecycle events: + +| State | Action | +|-------|--------| +| Resumed | Refresh data, reconnect WebSocket | +| Inactive | Pause non-essential operations | +| Paused | Save state, disconnect WebSocket | +| Detached | Clean up resources | +| Hidden | Similar to paused | + +--- + +[Previous: Authentication & Login](./login-flow.md) | [Next: Timeline & Photos](./timeline.md) diff --git a/docs/partners.md b/docs/partners.md new file mode 100644 index 0000000..30687e4 --- /dev/null +++ b/docs/partners.md @@ -0,0 +1,252 @@ +# Partners + +This document describes the partner sharing functionality. + +## Partner Overview + +Partners allow two-way photo sharing between users: +- Share your library with another user +- View photos from users who share with you +- Optionally include partner photos in your timeline + +## Partner Types + +### Shared With (Inbound) +Users who share their photos with you. +- You can view their photos +- Cannot modify their photos +- Can include in your timeline + +### Shared By (Outbound) +Users you share your photos with. +- They can view your photos +- Cannot modify your photos +- You control the sharing + +## Partner API + +### Get Partners Sharing With You +``` +GET /partners?direction=shared-with + +Response: +[ + { + "id": "user-uuid", + "email": "partner@example.com", + "name": "Jane Doe", + "profileImagePath": "/path/to/image", + "inTimeline": true + } +] +``` + +### Get Partners You Share With +``` +GET /partners?direction=shared-by + +Response: +[ + { + "id": "user-uuid", + "email": "friend@example.com", + "name": "John Smith", + "profileImagePath": "/path/to/image", + "inTimeline": false + } +] +``` + +### Add Partner (Share Your Library) +``` +POST /partners/{userId} + +Response: Partner user object +``` + +### Remove Partner +``` +DELETE /partners/{userId} +``` + +### Update Partner Settings +``` +PUT /partners/{userId} + +Body: +{ + "inTimeline": true +} +``` + +## Partner Screen + +### Layout +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Partners │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Sharing with you │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 Jane Doe │ │ +│ │ partner@example.com │ │ +│ │ Include in timeline: [Toggle: ON] [>] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 Bob Wilson │ │ +│ │ bob@example.com │ │ +│ │ Include in timeline: [Toggle: OFF] [>] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ You're sharing with │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 John Smith │ │ +│ │ friend@example.com [Remove] │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [+ Add Partner] │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Adding a Partner + +### User Selection Screen +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Select Partner │ +├─────────────────────────────────────────────────────────────┤ +│ [Search users...] │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Available Users │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 Alice Johnson │ │ +│ │ alice@example.com │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 👤 Charlie Brown │ │ +│ │ charlie@example.com │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Available Users API +``` +GET /users + +Returns users on the same server who can be added as partners +``` + +## Partner Detail View + +View a partner's photos: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Jane Doe's Photos │ +├─────────────────────────────────────────────────────────────┤ +│ December 2024 │ +│ ┌─────┬─────┬─────┬─────┐ │ +│ │ │ │ │ │ │ +│ │ 📷 │ 📷 │ 🎥 │ 📷 │ │ +│ │ │ │ │ │ │ +│ ├─────┼─────┼─────┼─────┤ │ +│ │ │ │ │ │ │ +│ │ 📷 │ 📷 │ 📷 │ 🎥 │ │ +│ │ │ │ │ │ │ +│ └─────┴─────┴─────┴─────┘ │ +│ ... │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Getting Partner's Assets +Partner assets are fetched during normal sync and filtered by user ID. + +## Timeline Integration + +### Include in Timeline +When `inTimeline: true`: +- Partner's photos appear in your main timeline +- Mixed chronologically with your photos +- Small avatar indicator shows owner + +### Multi-User Timeline +``` +Timeline Provider: + - If single user: Show only your assets + - If multiple users (partners with inTimeline): + - Fetch assets for all users + - Merge and sort chronologically + - Add user indicator to each asset +``` + +### Asset Owner Indicator +``` +┌─────┐ +│ 📷 │ +│ 👤 │ ← Small avatar showing partner +└─────┘ +``` + +## Partner Sync + +### How Partner Assets Sync +1. Fetch partner list on login +2. For each partner with `inTimeline: true`: + - Fetch their assets via sync API + - Store in local database with owner ID +3. Timeline queries include partner assets + +### Sync Endpoint +``` +GET /sync/full-sync?userId={partnerId} + +Or included in normal sync with partner IDs +``` + +## Privacy Considerations + +### What Partners Can See +- Your uploaded photos and videos +- Metadata (location, date, etc.) +- Albums you've shared + +### What Partners Cannot Do +- Delete your photos +- Edit your photos +- See your favorites/archive +- Access your locked folder + +## Removing a Partner + +### Stop Sharing Your Library +1. Go to Partners screen +2. Find user in "You're sharing with" +3. Tap Remove +4. Confirm removal + +### Behavior After Removal +- Partner can no longer see your photos +- Their local cache may still have thumbnails +- No data is deleted from your server + +## Partner vs Album Sharing + +| Feature | Partner Sharing | Album Sharing | +|---------|-----------------|---------------| +| Scope | Entire library | Specific album | +| New photos | Automatically included | Must add manually | +| Timeline integration | Optional | No | +| Bidirectional | Yes (if both share) | No | +| Granular control | No | Yes (roles) | + +--- + +[Previous: Sharing](./sharing.md) | [Next: Activities](./activities.md) diff --git a/docs/search.md b/docs/search.md new file mode 100644 index 0000000..7f882e7 --- /dev/null +++ b/docs/search.md @@ -0,0 +1,344 @@ +# Search + +This document describes the search functionality, including smart search, filters, and discovery features. + +## Search Overview + +The search feature allows users to find assets using: +- AI-powered semantic search (CLIP) +- Text-based search (filename, description, OCR) +- Filter-based search (people, location, camera, date, type) + +## Search Screen Layout + +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← [ Search photos and videos... 🔍 ] [ ⋮ ] │ +├─────────────────────────────────────────────────────────────┤ +│ [People] [Location] [Camera] [Date] [Type] [Display] → │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Search Results │ +│ or │ +│ Empty State │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Search Types + +### 1. Context Search (AI/CLIP) +- Default search mode +- Uses AI to understand image content +- Finds visually similar results + +**Examples:** +- "sunset on the beach" +- "birthday cake" +- "dog playing in park" +- "red car" + +### 2. Filename Search +- Searches asset filenames +- Partial matching supported + +**Examples:** +- "IMG_2024" +- "screenshot" +- ".png" + +### 3. Description Search +- Searches asset descriptions/captions +- User-added metadata + +### 4. OCR Search +- Searches text detected in images +- Useful for documents, signs, receipts + +**Examples:** +- "receipt" +- "invoice" +- Specific text in images + +## Search API + +### Main Search Endpoint +``` +POST /search/smart + +Body: +{ + "query": "sunset beach", + "page": 1, + "size": 100, + "type": "IMAGE" | "VIDEO" | null, + "isFavorite": true | false | null, + "isArchived": true | false | null, + "takenAfter": "2024-01-01T00:00:00Z", + "takenBefore": "2024-12-31T23:59:59Z", + "city": "San Francisco", + "state": "California", + "country": "United States", + "make": "Apple", + "model": "iPhone 15 Pro", + "personIds": ["person-uuid-1", "person-uuid-2"], + "language": "en-US" +} + +Response: +{ + "assets": { + "items": [...], + "nextPage": "2" + } +} +``` + +### Search Suggestions +``` +GET /search/suggestions?type={type}&country={country}&state={state}&make={make}&model={model} + +Types: country, state, city, cameraMake, cameraModel +``` + +## Filter Chips + +### People Filter +``` +┌─────────────────────────────────────────────────────────────┐ +│ People X │ +├─────────────────────────────────────────────────────────────┤ +│ Search people... │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ +│ │ 👤 │ │ 👤 │ │ 👤 │ │ 👤 │ │ 👤 │ │ +│ │ │ │ │ │ │ │ │ │ │ │ +│ │John │ │Jane │ │Bob │ │Alice│ │No │ │ +│ │ ✓ │ │ │ │ │ │ ✓ │ │Name │ │ +│ └─────┘ └─────┘ └─────┘ └─────┘ └─────┘ │ +│ │ +│ [Clear] [Search] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Location Filter +``` +┌─────────────────────────────────────────────────────────────┐ +│ Location X │ +├─────────────────────────────────────────────────────────────┤ +│ Country │ +│ [Select country... ▼] │ +│ │ +│ State │ +│ [Select state... ▼] │ +│ │ +│ City │ +│ [Select city... ▼] │ +│ │ +│ [Clear] [Search] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Camera Filter +``` +┌─────────────────────────────────────────────────────────────┐ +│ Camera X │ +├─────────────────────────────────────────────────────────────┤ +│ Make │ +│ [Select camera make... ▼] │ +│ │ +│ Model │ +│ [Select camera model... ▼] │ +│ │ +│ [Clear] [Search] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Date Filter +Uses native date range picker: +- Start date +- End date +- Presets: Today, This Week, This Month, This Year + +### Media Type Filter +``` +┌─────────────────────────────────────────────────────────────┐ +│ Media Type X │ +├─────────────────────────────────────────────────────────────┤ +│ ○ All │ +│ ○ Images only │ +│ ○ Videos only │ +│ │ +│ [Clear] [Search] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Display Options Filter +``` +┌─────────────────────────────────────────────────────────────┐ +│ Display Options X │ +├─────────────────────────────────────────────────────────────┤ +│ ☐ Not in any album │ +│ ☐ Archived only │ +│ ☐ Favorites only │ +│ │ +│ [Clear] [Search] │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Search Filter Model + +``` +SearchFilter { + // Text search + context: String? // AI search query + filename: String? // Filename search + description: String? // Description search + ocr: String? // OCR text search + + // Filters + people: Set // Selected people + location: LocationFilter { + country: String? + state: String? + city: String? + } + camera: CameraFilter { + make: String? + model: String? + } + date: DateFilter { + takenAfter: DateTime? + takenBefore: DateTime? + } + mediaType: AssetType // image, video, or other (all) + display: DisplayFilter { + isNotInAlbum: Boolean + isArchive: Boolean + isFavorite: Boolean + } + rating: RatingFilter // If rating feature enabled + language: String // For AI search localization +} +``` + +## Quick Links (Empty State) + +When search is empty, show quick access links: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ │ +│ [Polaroid Image] │ +│ │ +│ Search photos and videos │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ 🕐 Recently Taken │ │ +│ ├─────────────────────────────────────────────────────┤ │ +│ │ ▶️ Videos │ │ +│ ├─────────────────────────────────────────────────────┤ │ +│ │ ♥️ Favorites │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Explore Data + +### Explore API +``` +GET /search/explore + +Response: +[ + { + "fieldName": "exifInfo.city", + "items": [ + { + "value": "San Francisco", + "data": { asset preview data } + } + ] + }, + { + "fieldName": "smartInfo.objects", + "items": [...] + } +] +``` + +## Places / Map View + +### Get Assets by City +``` +GET /search/cities + +Response: Array of assets grouped by city +``` + +### Map Display +- Shows assets as markers on map +- Cluster markers for dense areas +- Tap cluster to zoom or view assets +- Filter by favorites, date range, partners + +## People Search + +### Get All People +``` +GET /people + +Response: +[ + { + "id": "person-uuid", + "name": "John Doe", + "birthDate": "1990-01-15", + "thumbnailPath": "/path/to/thumb", + "isHidden": false + } +] +``` + +### Person Detail +``` +GET /people/{id} + +Response: +{ + "id": "person-uuid", + "name": "John Doe", + "birthDate": "1990-01-15", + "thumbnailPath": "/path/to/thumb", + "assets": [...] +} +``` + +## Pagination + +- Search results are paginated +- Initial page: 1 +- Load more on scroll to bottom +- `nextPage` in response indicates more results + +## Search Result Actions + +Same as timeline multi-select: +- Add to album +- Favorite +- Archive +- Delete +- Share +- Download + +## Performance Considerations + +1. **Debounced input**: Wait for user to stop typing +2. **Cached suggestions**: Store frequently used +3. **Incremental loading**: Paginate results +4. **Cancel previous**: Cancel outdated requests + +--- + +[Previous: Timeline & Photos](./timeline.md) | [Next: Albums](./albums.md) diff --git a/docs/sharing.md b/docs/sharing.md new file mode 100644 index 0000000..752f8d5 --- /dev/null +++ b/docs/sharing.md @@ -0,0 +1,219 @@ +# Sharing + +This document describes sharing functionality including share links and external sharing. + +## Sharing Methods + +1. **Shared Links** - Public URLs for external sharing +2. **Album Sharing** - Share with other Immich users +3. **External Share** - System share sheet (export) + +## Shared Links + +### Creating a Shared Link + +#### For Individual Assets +``` +POST /shared-links + +Body: +{ + "type": "INDIVIDUAL", + "assetIds": ["asset-1", "asset-2", "asset-3"], + "description": "Vacation photos", + "expiresAt": "2024-12-31T23:59:59Z", + "allowDownload": true, + "allowUpload": false, + "showMetadata": true, + "password": "optional-password" +} +``` + +#### For Albums +``` +POST /shared-links + +Body: +{ + "type": "ALBUM", + "albumId": "album-uuid", + "description": "Summer 2024", + "expiresAt": null, // Never expires + "allowDownload": true, + "allowUpload": true, // Allow contributions + "showMetadata": false, + "password": null +} +``` + +### Shared Link Options + +| Option | Description | +|--------|-------------| +| description | Link description/title | +| expiresAt | Expiration date (null = never) | +| allowDownload | Recipients can download | +| allowUpload | Recipients can add photos (albums only) | +| showMetadata | Show EXIF data | +| password | Password protection | +| slug | Custom URL slug | + +### Shared Link URL Format +``` +https://{server}/share/{link-id} +https://{server}/share/{custom-slug} +``` + +### Link Creation UI +``` +┌─────────────────────────────────────────────────────────────┐ +│ ← Create Shared Link │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ Description │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Vacation photos │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ Expires │ +│ [Never ▼] │ +│ │ +│ ──────────────────────────────────────────────────── │ +│ │ +│ Allow Download [Toggle: ON] │ +│ Allow Upload (albums only) [Toggle: OFF] │ +│ Show Metadata [Toggle: ON] │ +│ │ +│ ──────────────────────────────────────────────────── │ +│ │ +│ Password Protection │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ (optional) │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ +│ [Create Link] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Managing Shared Links + +#### List All Links +``` +GET /shared-links + +Response: +[ + { + "id": "link-uuid", + "description": "Vacation photos", + "type": "INDIVIDUAL", + "assets": [...], + "expiresAt": "2024-12-31T23:59:59Z", + "allowDownload": true, + "allowUpload": false, + "showMetadata": true, + "createdAt": "2024-06-01T10:00:00Z" + } +] +``` + +#### Update Link +``` +PATCH /shared-links/{id} + +Body: +{ + "description": "Updated description", + "expiresAt": "2025-01-31T23:59:59Z", + "allowDownload": false, + "changeExpiryTime": true +} +``` + +#### Delete Link +``` +DELETE /shared-links/{id} +``` + +## External Sharing (Export) + +### Share to Other Apps +Uses system share sheet to send assets to other apps. + +### Share Flow +``` +1. User selects assets +2. Tap share button +3. For local assets: Use file directly +4. For remote assets: Download to temp folder +5. Open system share sheet +6. User selects destination app +7. Clean up temp files +``` + +### Share Sheet +``` +┌─────────────────────────────────────────────────────────────┐ +│ Share via │ +├─────────────────────────────────────────────────────────────┤ +│ [Messages] [WhatsApp] [Email] [AirDrop] [More...] │ +├─────────────────────────────────────────────────────────────┤ +│ [Copy] [Save Image] [Print] [Assign to Contact] │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Code Flow +``` +1. Get selected assets +2. For each asset: + - If local: get file path + - If remote: download to temp +3. Create share data with file list +4. Present share sheet +5. Handle completion +``` + +## Album Sharing + +See [Albums Documentation](./albums.md) for: +- Sharing albums with users +- User roles (viewer/editor) +- Managing shared album members + +## Share Intent (Receiving) + +### Receiving Shared Content +When other apps share to Immich: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Upload to Immich │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ [Preview of shared content] │ +│ 3 photos ready to upload │ +│ │ +├─────────────────────────────────────────────────────────────┤ +│ Add to Album (optional) │ +│ [Select album... ▼] │ +├─────────────────────────────────────────────────────────────┤ +│ │ +│ [Cancel] [Upload] │ +│ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Share Intent Flow +``` +1. Receive share intent from system +2. Extract shared files/URIs +3. Show upload preview screen +4. User confirms upload +5. Upload assets to server +6. Optionally add to album +7. Return to source app +``` + +--- + +[Previous: Download](./download.md) | [Next: Partners](./partners.md) diff --git a/docs/structure.md b/docs/structure.md new file mode 100644 index 0000000..34e6ca9 --- /dev/null +++ b/docs/structure.md @@ -0,0 +1,288 @@ +# Project Structure + +This document explains the organization of the mobile app codebase. + +## Top-Level Directory Structure + +``` +mobile/ +├── lib/ # Main application code +├── assets/ # Static assets (images, fonts, animations) +├── fonts/ # Custom fonts +├── openapi/ # Auto-generated API client +├── integration_test/ # Integration tests +├── test/ # Unit tests +├── android/ # Android platform code +├── ios/ # iOS platform code +└── pigeon/ # Native platform interface definitions +``` + +## Main Source Directory (`lib/`) + +### Core Application +``` +lib/ +├── main.dart # App entry point +├── constants/ # App-wide constants +│ ├── constants.dart # General constants +│ ├── enums.dart # Enumerations +│ ├── errors.dart # Error definitions +│ ├── colors.dart # Color definitions +│ └── locales.dart # Supported locales +``` + +### Domain Layer +``` +lib/domain/ +├── interfaces/ # Abstract interfaces +├── models/ # Domain models +│ ├── album/ # Album models +│ ├── asset/ # Asset models +│ ├── user.model.dart # User model +│ ├── person.model.dart # Person (face) model +│ ├── memory.model.dart # Memory model +│ └── ... +├── services/ # Domain services +│ ├── asset.service.dart +│ ├── timeline.service.dart +│ ├── local_sync.service.dart +│ └── ... +└── utils/ # Domain utilities +``` + +### Services Layer +``` +lib/services/ +├── api.service.dart # API client wrapper +├── auth.service.dart # Authentication +├── backup.service.dart # Photo backup +├── album.service.dart # Album management +├── search.service.dart # Search functionality +├── download.service.dart # Asset download +├── share.service.dart # Sharing +├── trash.service.dart # Trash management +├── memory.service.dart # Memories +├── partner.service.dart # Partner sharing +├── background.service.dart # Background tasks +└── ... +``` + +### Repository Layer +``` +lib/repositories/ +├── api.repository.dart # Base API repository +├── asset.repository.dart # Asset data access +├── album.repository.dart # Album data access +├── auth.repository.dart # Auth data access +├── auth_api.repository.dart # Auth API calls +├── album_api.repository.dart # Album API calls +├── asset_api.repository.dart # Asset API calls +├── backup.repository.dart # Backup state +├── download.repository.dart # Download management +└── ... +``` + +### Infrastructure Layer +``` +lib/infrastructure/ +├── entities/ # Database entities +│ ├── remote_asset.entity.dart +│ ├── local_asset.entity.dart +│ ├── remote_album.entity.dart +│ ├── user.entity.dart +│ └── ... +├── repositories/ # Infrastructure repositories +│ ├── db.repository.dart # Database access +│ ├── api.repository.dart # HTTP client +│ └── ... +├── loaders/ # Image loaders +│ ├── image_request.dart +│ ├── local_image_request.dart +│ └── remote_image_request.dart +└── utils/ # Infrastructure utilities +``` + +### State Management (Providers) +``` +lib/providers/ +├── api.provider.dart # API service provider +├── auth.provider.dart # Auth state +├── asset.provider.dart # Asset state +├── db.provider.dart # Database provider +├── websocket.provider.dart # WebSocket connection +├── theme.provider.dart # Theme state +├── user.provider.dart # User state +├── album/ # Album providers +├── backup/ # Backup providers +├── search/ # Search providers +├── timeline/ # Timeline providers +├── asset_viewer/ # Asset viewer providers +└── ... +``` + +### UI Layer - Pages +``` +lib/pages/ +├── login/ +│ ├── login.page.dart # Login screen +│ └── change_password.page.dart +├── photos/ +│ ├── photos.page.dart # Main timeline +│ └── memory.page.dart # Memory view +├── search/ +│ ├── search.page.dart # Search screen +│ ├── all_people.page.dart # People browser +│ ├── all_places.page.dart # Places browser +│ └── map/ # Map views +├── albums/ +│ └── albums.page.dart # Albums list +├── album/ +│ ├── album_viewer.page.dart # Album detail +│ ├── album_options.page.dart # Album settings +│ └── ... +├── library/ +│ ├── library.page.dart # Library home +│ ├── favorite.page.dart # Favorites +│ ├── archive.page.dart # Archive +│ ├── trash.page.dart # Trash +│ ├── locked/ # Locked folder +│ ├── partner/ # Partner pages +│ └── ... +├── backup/ +│ ├── backup_controller.page.dart +│ ├── backup_album_selection.page.dart +│ └── ... +├── common/ +│ ├── splash_screen.page.dart # Splash screen +│ ├── settings.page.dart # Settings +│ ├── gallery_viewer.page.dart# Asset viewer +│ └── ... +├── editing/ +│ ├── edit.page.dart # Image editor +│ ├── crop.page.dart # Crop tool +│ └── filter.page.dart # Filters +└── onboarding/ + └── permission_onboarding.page.dart +``` + +### UI Layer - Widgets +``` +lib/widgets/ +├── common/ # Shared widgets +│ ├── immich_app_bar.dart +│ ├── immich_image.dart +│ ├── immich_thumbnail.dart +│ ├── immich_loading_indicator.dart +│ └── ... +├── asset_grid/ # Grid components +│ ├── multiselect_grid.dart +│ ├── asset_grid_data_structure.dart +│ └── ... +├── asset_viewer/ # Viewer components +│ ├── gallery_app_bar.dart +│ ├── bottom_gallery_bar.dart +│ ├── detail_panel/ +│ └── ... +├── album/ # Album components +├── backup/ # Backup components +├── search/ # Search components +├── settings/ # Settings components +├── memories/ # Memory components +├── map/ # Map components +└── forms/ # Form components + └── login/ + └── login_form.dart +``` + +### Routing +``` +lib/routing/ +├── router.dart # Route definitions +├── auth_guard.dart # Authentication guard +├── duplicate_guard.dart # Prevent duplicate routes +├── backup_permission_guard.dart +├── gallery_guard.dart +└── locked_guard.dart +``` + +### Models +``` +lib/models/ +├── auth/ +│ ├── login_response.model.dart +│ └── auxilary_endpoint.model.dart +├── backup/ +│ ├── backup_candidate.model.dart +│ ├── current_upload_asset.model.dart +│ └── error_upload_asset.model.dart +├── albums/ +├── search/ +├── shared_link/ +├── upload/ +└── ... +``` + +### Utilities +``` +lib/utils/ +├── bootstrap.dart # App initialization +├── image_url_builder.dart # URL construction +├── hash.dart # Hashing utilities +├── debounce.dart # Debounce helper +├── throttle.dart # Throttle helper +├── diff.dart # List diffing +├── migration.dart # Database migration +├── hooks/ # Custom hooks +└── cache/ # Caching utilities +``` + +### Extensions +``` +lib/extensions/ +├── asset_extensions.dart +├── string_extensions.dart +├── datetime_extensions.dart +├── build_context_extensions.dart +└── ... +``` + +### Theming +``` +lib/theme/ +├── theme_data.dart # Theme definitions +├── color_scheme.dart # Color schemes +└── dynamic_theme.dart # Dynamic theming +``` + +### Platform Communication +``` +lib/platform/ +├── background_worker_api.g.dart +├── connectivity_api.g.dart +├── local_image_api.g.dart +├── network_api.g.dart +└── ... +``` + +## Generated Code + +Some code is auto-generated: +- `openapi/` - API client from OpenAPI spec +- `*.g.dart` - Code generation (serialization, providers) +- `*.drift.dart` - Database schema + +## Test Structure + +``` +test/ +├── services/ # Service tests +├── domain/ # Domain tests +├── infrastructure/ # Repository tests +├── modules/ # Feature module tests +├── fixtures/ # Test data stubs +└── mocks/ # Mock objects +``` + +--- + +[Previous: Architecture Overview](./architecture.md) | [Next: Authentication & Login](./login-flow.md) diff --git a/docs/timeline.md b/docs/timeline.md new file mode 100644 index 0000000..a1983bf --- /dev/null +++ b/docs/timeline.md @@ -0,0 +1,257 @@ +# Timeline & Photos + +This document describes the photo timeline, asset display, and related functionality. + +## Timeline Overview + +The timeline is the main view of the app, displaying all photos and videos in chronological order (newest first). + +## Asset Types + +### Asset States +``` +┌─────────────────────────────────────────────────────────────┐ +│ Asset States │ +├───────────────┬───────────────┬─────────────────────────────┤ +│ LOCAL │ REMOTE │ MERGED │ +├───────────────┼───────────────┼─────────────────────────────┤ +│ On device │ On server │ Both device and server │ +│ Not uploaded │ Not on device │ Linked by checksum │ +│ localId set │ remoteId set │ Both IDs set │ +└───────────────┴───────────────┴─────────────────────────────┘ +``` + +### Asset Model +``` +Asset { + id: String // Unique identifier + name: String // Filename + type: AssetType // image, video, audio, other + createdAt: DateTime // When created/taken + updatedAt: DateTime // Last modified + checksum: String? // File hash for deduplication + width: Integer? // Image/video width + height: Integer? // Image/video height + durationInSeconds: Int? // Video duration + isFavorite: Boolean // Marked as favorite + isArchived: Boolean // Hidden from timeline + isTrashed: Boolean // In trash + livePhotoVideoId: String? // Paired video for live photos + + // State indicators + localId: String? // Device gallery ID + remoteId: String? // Server asset ID + storage: AssetState // local, remote, or merged +} +``` + +### Asset Types Enum +| Type | Description | +|------|-------------| +| image | Photos (JPEG, PNG, HEIC, etc.) | +| video | Videos (MP4, MOV, etc.) | +| audio | Audio files | +| other | Other file types | + +### Playback Styles +| Style | Description | +|-------|-------------| +| image | Static image | +| video | Full video | +| imageAnimated | GIF or animated image | +| livePhoto | Photo with motion component | +| videoLooping | Short looping video | + +## Timeline Display + +### Grid Configuration +- **Tiles per row**: Configurable (default: 4) +- **Dynamic layout**: Optional variable row heights +- **Group by**: Day, Month, or None + +### Date Headers +Assets are grouped by date with section headers: +``` +December 2024 +├── Dec 15 (if grouping by day) +│ └── [asset grid] +├── Dec 14 +│ └── [asset grid] +... +``` + +### Asset Thumbnail Display +Each asset shows: +- Thumbnail image +- Video duration (if video) +- Live photo indicator (if motion photo) +- Favorite indicator (heart icon) +- Selection checkbox (in multi-select mode) +- Stack indicator (if part of stack) + +## Loading Strategy + +### Pagination +- Timeline loads in batches (default: 1024 assets) +- Additional assets loaded when scrolling +- Opposite direction pre-loading (64 assets) + +### Thumbnail Loading +1. Check local cache first +2. Load from local device if available +3. Fetch from server with thumbhash placeholder +4. Cache for future use + +### Image Quality Levels +| Level | Use Case | Resolution | +|-------|----------|------------| +| Thumbhash | Instant placeholder | ~4x4 blurred | +| Thumbnail | Grid view | ~250px | +| Preview | Full screen initial | ~1080px | +| Original | Full zoom | Full resolution | + +## Multi-User Timeline + +When partner sharing is enabled: +- Timeline can include multiple users' assets +- Assets are merged chronologically +- User indicator shows asset owner + +## Multi-Select Mode + +### Activation +- Long press on any asset +- Or tap select button in app bar + +### Available Actions +``` +┌─────────────────────────────────────────────────────────────┐ +│ [X] Cancel Selected: 5 │ +├─────────────────────────────────────────────────────────────┤ +│ ♥ Favorite │ 📦 Archive │ 🗑 Delete │ 📤 Share │ ⋮ │ +└─────────────────────────────────────────────────────────────┘ +``` + +### Actions Available +| Action | Description | +|--------|-------------| +| Favorite | Toggle favorite status | +| Archive | Move to archive | +| Delete | Move to trash | +| Share | Share via system share | +| Download | Download to device | +| Add to Album | Add to existing/new album | +| Create Stack | Stack selected assets | +| Move to Locked | Move to locked folder | + +## Pull to Refresh + +### Refresh Behavior +1. Single pull: Incremental sync +2. Double pull (within 4 seconds): Full refresh + - Clears local cache + - Re-fetches all data + +### Sync Process +1. Check server for new assets +2. Download metadata for new assets +3. Update local database +4. Refresh album data +5. Update UI + +## Memory Lane + +### Display +- Horizontal scrolling widget at top of timeline +- Shows "X years ago" cards +- Only visible if user has memories enabled + +### Memory Card +``` +┌─────────────────┐ +│ [Thumbnail] │ +│ │ +│ 2 years ago │ +└─────────────────┘ +``` + +### Memory API +``` +GET /memories?for={today's date} + +Response: Array of Memory objects +{ + "id": "memory-id", + "data": { + "year": 2022 + }, + "assets": [...] +} +``` + +## Scroll Scrubber + +### Features +- Appears on right side when scrolling +- Shows date labels +- Drag to quickly navigate +- Snaps to month boundaries (if enough history) + +### Configuration +- Enabled when timeline spans 12+ months +- Custom scroll controller for smooth navigation + +## Asset Visibility + +Assets can have different visibility states: + +| State | Timeline | Archive | Trash | Locked | +|-------|----------|---------|-------|--------| +| Normal | ✓ | | | | +| Archived | | ✓ | | | +| Trashed | | | ✓ | | +| Hidden/Locked | | | | ✓ | + +## Asset Stacks + +Multiple similar assets can be "stacked": +- Only primary asset shown in timeline +- Stack indicator shows count +- Tap to view stack contents +- Can unstack to separate + +## Real-time Updates + +Via WebSocket connection: +- New uploads appear immediately +- Deletions remove assets from view +- Updates refresh asset metadata + +### WebSocket Events +| Event | Action | +|-------|--------| +| on_upload_success | Add new asset to timeline | +| on_asset_delete | Remove asset | +| on_asset_trash | Move to trash | +| on_asset_restore | Restore from trash | +| on_asset_update | Refresh metadata | +| on_asset_hidden | Hide from timeline | + +## Performance Optimizations + +1. **Virtualized list**: Only renders visible items +2. **Image caching**: Configurable cache size +3. **Lazy loading**: Load data as needed +4. **Debounced scroll**: Reduce render calls +5. **Background prefetching**: Load ahead + +### Cache Settings +| Setting | Default | Description | +|---------|---------|-------------| +| thumbnailCacheSize | 10000 | Max cached thumbnails | +| imageCacheSize | 350 | Max cached full images | +| albumThumbnailCacheSize | 200 | Max album thumbs | + +--- + +[Previous: Main Navigation](./main-screens.md) | [Next: Search](./search.md)