add docs part 1
This commit is contained in:
@@ -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
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user