add docs part 1

This commit is contained in:
2026-03-26 17:40:00 +05:00
parent ad9d762ae2
commit f000d7265d
14 changed files with 3852 additions and 0 deletions
+72
View File
@@ -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)
+355
View File
@@ -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<User> // 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)
+173
View File
@@ -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)
+339
View File
@@ -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)
+214
View File
@@ -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)
+293
View File
@@ -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)
+422
View File
@@ -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<Asset> // 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)
+362
View File
@@ -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)
+262
View File
@@ -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)
+252
View File
@@ -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)
+344
View File
@@ -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<Person> // 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)
+219
View File
@@ -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)
+288
View File
@@ -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)
+257
View File
@@ -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)