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