add docs part 2

This commit is contained in:
2026-03-26 22:51:40 +05:00
parent f000d7265d
commit e820194aa0
10 changed files with 3440 additions and 0 deletions

202
docs/activities.md Normal file
View File

@@ -0,0 +1,202 @@
# Activities - Comments and Likes
Activities allow users to interact with assets in shared albums through comments and likes. This creates a social layer for collaborative photo sharing.
## Activity Types
### Comment
A text-based reaction attached to an album or specific asset:
- **id**: Unique identifier
- **assetId**: Optional - if set, comment is on specific asset; otherwise on album
- **comment**: The text content
- **createdAt**: Timestamp when comment was created
- **user**: The user who created the comment
### Like
A simple thumbs-up reaction:
- **id**: Unique identifier
- **assetId**: Optional - if set, like is on specific asset; otherwise on album
- **createdAt**: Timestamp when like was created
- **user**: The user who liked
## Activity Statistics
The app can fetch activity statistics for an album:
- **comments**: Number of comments on the album
## API Endpoints
### Get All Activities
Fetch all activities for an album, optionally filtered to a specific asset.
**Request:**
```
GET /activities?albumId={albumId}&assetId={assetId}
```
**Response:**
```json
[
{
"id": "activity-uuid",
"albumId": "album-uuid",
"assetId": "asset-uuid",
"comment": "Great photo!",
"type": "comment",
"createdAt": "2024-01-15T10:30:00Z",
"user": {
"id": "user-uuid",
"name": "John Doe",
"email": "john@example.com"
}
}
]
```
### Create Activity
Add a new comment or like.
**Request:**
```
POST /activities
{
"albumId": "album-uuid",
"assetId": "asset-uuid",
"type": "comment",
"comment": "Great photo!"
}
```
**Response:** The created activity object
### Delete Activity
Remove an activity (comment or like).
**Request:**
```
DELETE /activities/{id}
```
### Get Activity Statistics
Get comment count for an album.
**Request:**
```
GET /activities/statistics?albumId={albumId}&assetId={assetId}
```
**Response:**
```json
{
"comments": 5
}
```
## UI Components
### Activities Page
The main activities page displays all activities for an album or asset.
**Layout:**
```
+----------------------------------+
| [Back] Album Name |
+----------------------------------+
| |
| [Avatar] User • 2 hours ago |
| Comment text here |
| |
| [Thumbs Up] User • 1 hour ago |
| |
| [Avatar] User • 30 min ago |
| Another comment |
| |
| ... (scrollable list) |
| |
+----------------------------------+
| [Avatar] [Text Input] [Like] |
+----------------------------------+
```
**Behaviors:**
- List scrolls to show all activities
- New comments auto-scroll to bottom
- Activities can be dismissed by swiping (if user has permission)
### Activity Tile
Each activity is displayed as a tile:
**Comment Tile:**
- User avatar on the left
- Username and relative time ("2 hours ago") in header
- Comment text as subtitle
- If viewing from album page: thumbnail of asset on right (tappable)
**Like Tile:**
- Thumbs up icon on the left (colored with primary theme color)
- Username and relative time
- If on specific asset: shows asset thumbnail
### Activity Text Field
Input area at bottom of activities page:
**Components:**
- Current user's avatar on the left
- Text input field in center
- Like/unlike button on the right
**States:**
- **Enabled**: User can type comments and toggle like
- **Disabled**: Shows message "Activities are disabled" (when album owner disables activities)
**Like Button States:**
- **Not liked**: Outline thumbs up icon
- **Liked**: Filled thumbs up icon in primary color
**Behaviors:**
- Keyboard shows automatically when page opens
- Pressing "Send" on keyboard submits comment
- Tapping outside unfocuses input
- Like button toggles current user's like on/off
### Dismissible Activity
Activities can be swiped to delete if user has permission:
- Activity author can delete their own activities
- Album owner can delete any activity
**Swipe Animation:**
- Red background appears
- Trash icon shown
- Activity slides away when dismissed
## Permission Rules
| Action | Who Can Perform |
|--------|-----------------|
| Add comment | Any album member (if activities enabled) |
| Add like | Any album member (if activities enabled) |
| Delete own activity | Activity author |
| Delete any activity | Album owner |
| Disable activities | Album owner |
## Localization Strings
| Key | Usage |
|-----|-------|
| `say_something` | Placeholder text in comment input |
| `shared_album_activities_input_disable` | Shown when activities are disabled |
## Navigation
When viewing activities from album page and tapping an activity with an asset:
1. Build asset viewer route
2. Navigate to full-screen asset viewer
3. Show the specific asset the activity references
---
[Previous: Partners](partners.md) | [Next: Background Services](background-services.md)

607
docs/api-reference.md Normal file
View File

@@ -0,0 +1,607 @@
# API Reference
The mobile app communicates with the server through a REST API and WebSocket connection. This document covers all API endpoints used by the app.
## API Service Structure
The API is organized into domain-specific sub-APIs:
| API | Purpose |
|-----|---------|
| authenticationApi | Login, logout, session management |
| usersApi | User profile and settings |
| assetsApi | Asset CRUD operations |
| albumsApi | Album management |
| searchApi | Smart search and filters |
| partnersApi | Partner sharing |
| sharedLinksApi | External share links |
| activitiesApi | Comments and likes |
| peopleApi | Face recognition and people |
| memoriesApi | Memory lane feature |
| trashApi | Trash management |
| serverApi | Server info and config |
| oauthApi | OAuth authentication |
| downloadApi | Asset downloads |
| syncApi | Data synchronization |
## Authentication
### Login
```
POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
Response:
{
"accessToken": "jwt-token",
"userId": "user-uuid",
"userEmail": "user@example.com",
"name": "John Doe",
"isAdmin": false,
"profileImagePath": "/path/to/image"
}
```
### Logout
```
POST /auth/logout
Authorization: Bearer {token}
Response: 204 No Content
```
### Validate Token
```
POST /auth/validateToken
Authorization: Bearer {token}
Response:
{
"authStatus": true
}
```
### Change Password
```
POST /auth/change-password
Authorization: Bearer {token}
Content-Type: application/json
{
"password": "current-password",
"newPassword": "new-password"
}
```
## OAuth
### Get OAuth Config
```
GET /oauth/config
Response:
{
"enabled": true,
"autoRegister": true,
"autoLaunch": false,
"buttonText": "Login with SSO"
}
```
### Start OAuth Flow
```
POST /oauth/authorize
Content-Type: application/json
{
"redirectUri": "app://oauth-callback",
"codeChallenge": "challenge-string",
"codeChallengeMethod": "S256"
}
Response:
{
"url": "https://oauth-provider.com/authorize?..."
}
```
### Complete OAuth
```
POST /oauth/callback
Content-Type: application/json
{
"url": "app://oauth-callback?code=auth-code",
"codeVerifier": "verifier-string"
}
Response:
{
"accessToken": "jwt-token",
"userId": "user-uuid",
...
}
```
## Assets
### Get All Assets
```
GET /assets?updatedAfter={timestamp}&updatedBefore={timestamp}
Authorization: Bearer {token}
Response:
[
{
"id": "asset-uuid",
"ownerId": "user-uuid",
"checksum": "sha1-hash",
"fileCreatedAt": "2024-01-15T10:30:00Z",
"fileModifiedAt": "2024-01-15T10:30:00Z",
"type": "IMAGE",
"originalFileName": "IMG_1234.jpg",
"isFavorite": false,
"isArchived": false,
"isTrashed": false,
"thumbhash": "base64-thumbhash",
"exifInfo": { ... }
}
]
```
### Upload Asset
```
POST /assets
Authorization: Bearer {token}
Content-Type: multipart/form-data
Form fields:
- assetData: File binary
- deviceAssetId: Device-specific ID
- deviceId: Device identifier
- fileCreatedAt: Original creation time
- fileModifiedAt: Original modification time
- duration: Video duration (if applicable)
- isFavorite: Boolean
- isArchived: Boolean
- isVisible: Boolean
Response:
{
"id": "asset-uuid",
"duplicate": false
}
```
### Get Asset Info
```
GET /assets/{id}
Authorization: Bearer {token}
Response: Asset object
```
### Update Asset
```
PUT /assets/{id}
Authorization: Bearer {token}
Content-Type: application/json
{
"isFavorite": true,
"isArchived": false,
"description": "Updated description"
}
```
### Delete Assets
```
DELETE /assets
Authorization: Bearer {token}
Content-Type: application/json
{
"ids": ["asset-uuid-1", "asset-uuid-2"],
"force": false
}
```
### Get Asset Thumbnail
```
GET /assets/{id}/thumbnail?size={thumbnail|preview}
Authorization: Bearer {token}
Response: Image binary
```
### Get Asset Original
```
GET /assets/{id}/original
Authorization: Bearer {token}
Response: Original file binary
```
### Get Video Playback
```
GET /assets/{id}/video/playback
Authorization: Bearer {token}
Response: Video stream
```
### Check Duplicates
```
POST /assets/bulk-upload-check
Authorization: Bearer {token}
Content-Type: application/json
{
"assets": [
{
"id": "device-asset-id",
"checksum": "sha1-hash"
}
]
}
Response:
{
"results": [
{
"id": "device-asset-id",
"action": "accept|reject",
"reason": "duplicate"
}
]
}
```
## Albums
### Get All Albums
```
GET /albums?shared={true|false}
Authorization: Bearer {token}
Response:
[
{
"id": "album-uuid",
"ownerId": "user-uuid",
"albumName": "Vacation 2024",
"createdAt": "2024-01-15T10:30:00Z",
"assetCount": 50,
"shared": true,
"albumThumbnailAssetId": "asset-uuid"
}
]
```
### Create Album
```
POST /albums
Authorization: Bearer {token}
Content-Type: application/json
{
"albumName": "New Album",
"assetIds": ["asset-uuid-1", "asset-uuid-2"]
}
Response: Album object
```
### Get Album Details
```
GET /albums/{id}
Authorization: Bearer {token}
Response: Album object with assets
```
### Update Album
```
PATCH /albums/{id}
Authorization: Bearer {token}
Content-Type: application/json
{
"albumName": "Updated Name",
"activityEnabled": true
}
```
### Delete Album
```
DELETE /albums/{id}
Authorization: Bearer {token}
```
### Add Assets to Album
```
PUT /albums/{id}/assets
Authorization: Bearer {token}
Content-Type: application/json
{
"ids": ["asset-uuid-1", "asset-uuid-2"]
}
```
### Remove Assets from Album
```
DELETE /albums/{id}/assets
Authorization: Bearer {token}
Content-Type: application/json
{
"ids": ["asset-uuid-1", "asset-uuid-2"]
}
```
### Add Users to Album
```
PUT /albums/{id}/users
Authorization: Bearer {token}
Content-Type: application/json
{
"sharedUserIds": ["user-uuid-1"],
"albumUserRoles": [{"userId": "user-uuid-1", "role": "editor"}]
}
```
## Search
### Smart Search
```
POST /search/smart
Authorization: Bearer {token}
Content-Type: application/json
{
"query": "beach sunset",
"page": 1,
"size": 100,
"type": "IMAGE"
}
Response:
{
"assets": {
"items": [...],
"total": 150,
"count": 100
}
}
```
### Metadata Search
```
POST /search/metadata
Authorization: Bearer {token}
Content-Type: application/json
{
"originalFileName": "IMG",
"city": "New York",
"make": "Apple",
"takenAfter": "2024-01-01",
"takenBefore": "2024-12-31"
}
```
### Get Search Suggestions
```
GET /search/suggestions?query={text}&type={type}
Authorization: Bearer {token}
Response:
["suggestion1", "suggestion2", ...]
```
## Partners
### Get Partners
```
GET /partners?direction={shared-by|shared-with}
Authorization: Bearer {token}
Response:
[
{
"id": "user-uuid",
"email": "partner@example.com",
"name": "Partner Name",
"inTimeline": true
}
]
```
### Create Partnership
```
POST /partners/{userId}
Authorization: Bearer {token}
Response: Partner object
```
### Update Partnership
```
PUT /partners/{userId}
Authorization: Bearer {token}
Content-Type: application/json
{
"inTimeline": true
}
```
### Remove Partnership
```
DELETE /partners/{userId}
Authorization: Bearer {token}
```
## Shared Links
### Create Shared Link
```
POST /shared-links
Authorization: Bearer {token}
Content-Type: application/json
{
"type": "ALBUM|INDIVIDUAL",
"albumId": "album-uuid",
"assetIds": ["asset-uuid"],
"allowUpload": false,
"allowDownload": true,
"showMetadata": true,
"password": "optional-password",
"expiresAt": "2024-12-31T23:59:59Z"
}
Response:
{
"id": "link-uuid",
"key": "share-key",
"url": "https://server/share/share-key"
}
```
### Get My Shared Links
```
GET /shared-links/me
Authorization: Bearer {token}
Response: Array of shared links
```
### Delete Shared Link
```
DELETE /shared-links/{id}
Authorization: Bearer {token}
```
## Server
### Get Server Info
```
GET /server/info
Authorization: Bearer {token}
Response:
{
"version": "1.2.3",
"diskAvailable": "50 GB",
"diskSize": "100 GB",
"diskUsagePercentage": 50
}
```
### Get Server Features
```
GET /server/features
Authorization: Bearer {token}
Response:
{
"clipEncode": true,
"facialRecognition": true,
"map": true,
"trash": true,
"oauth": true,
"oauthAutoLaunch": false,
"passwordLogin": true
}
```
### Get Server Config
```
GET /server/config
Response:
{
"loginPageMessage": "Welcome",
"trashDays": 30,
"isInitialized": true
}
```
### Ping Server
```
GET /server/ping
Response:
{
"res": "pong"
}
```
## Error Responses
All endpoints may return error responses:
```
{
"error": "Error Type",
"statusCode": 400,
"message": "Detailed error message"
}
```
Common status codes:
- 400: Bad Request
- 401: Unauthorized
- 403: Forbidden
- 404: Not Found
- 500: Internal Server Error
---
[Previous: Data Models](data-models.md) | [Next: Local Storage](local-storage.md)

261
docs/background-services.md Normal file
View File

@@ -0,0 +1,261 @@
# Background Services
The app runs various background services to sync photos, perform backups, and maintain data consistency without requiring user interaction.
## Background Backup Service
The background backup service automatically uploads photos to the server when the app is not in the foreground.
### Platform Channel Communication
The app uses platform-specific channels to communicate with native background services:
**Channel Name:** `immich/foregroundChannel`
**Methods:**
| Method | Direction | Purpose |
|--------|-----------|---------|
| `initialized` | Native → App | Notify app that background service started |
| `start` | App → Native | Start background backup |
| `stop` | App → Native | Stop background backup |
| `updateNotification` | App → Native | Update progress notification |
| `systemStop` | Native → App | System stopped the service |
### Notification Structure
Background backup shows progress notifications:
**Notification Data:**
```json
{
"title": "Backup Progress",
"content": "Uploading photo 5 of 100",
"progress": 5,
"max": 100,
"indeterminate": false,
"isDetail": true
}
```
**Notification Types:**
- **Total Progress**: Overall backup progress (X of Y files)
- **Single Progress**: Current file upload progress (percentage)
- **Error Notification**: Shows when upload fails
### Background Service Lifecycle
```
App Backgrounded
┌─────────────────┐
│ Service Starts │
│ (initialized) │
└────────┬────────┘
┌─────────────────┐
│ Check Conditions│
│ - WiFi required?│
│ - Charging? │
└────────┬────────┘
┌────┴────┐
│ │
▼ ▼
Proceed Wait
┌─────────────────┐
│ Find Upload │
│ Candidates │
└────────┬────────┘
┌─────────────────┐
│ Upload Files │◄──────┐
│ One by One │ │
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ Update Notif. │───────┘
│ Progress │
└────────┬────────┘
┌────┴────┐
│ │
▼ ▼
More Complete
Files │
│ ▼
│ ┌─────────────────┐
└───►│ Service Stops │
└─────────────────┘
```
### Settings
| Setting | Description | Default |
|---------|-------------|---------|
| `backgroundBackup` | Enable/disable background backup | false |
| `backupRequireWifi` | Only backup on WiFi | true |
| `backupRequireCharging` | Only backup while charging | false |
| `backupTriggerDelay` | Minutes before starting backup | 30 |
| `backgroundBackupTotalProgress` | Show total progress notification | true |
| `backgroundBackupSingleProgress` | Show per-file progress | false |
### Error Handling
**Grace Period for Notifications:**
- Errors don't immediately show notification
- `uploadErrorNotificationGracePeriod` setting controls delay (default: 2 uploads)
- Prevents notification spam for transient errors
**Error Recovery:**
- Service tracks `backupFailedSince` timestamp
- Retries on next service run
- User notified after grace period exceeded
## Sync Service
Keeps local database synchronized with server.
### Sync Operations
1. **Asset Sync**: Download remote asset metadata
2. **Album Sync**: Sync album memberships and contents
3. **User Sync**: Sync user and partner information
### Sync Flow
```
┌─────────────────┐
│ Sync Trigger │
│ (App Start / │
│ WebSocket) │
└────────┬────────┘
┌─────────────────┐
│ Fetch Remote │
│ Changes (ETag) │
└────────┬────────┘
┌────┴────┐
│ │
▼ ▼
Changes No
Found Changes
│ │
▼ │
┌─────────────────┐
│ Process Deltas │
│ - New assets │
│ - Updated assets│
│ - Deleted assets│
└────────┬────────┘
┌─────────────────┐
│ Update Local DB │
└────────┬────────┘
┌─────────────────┐
│ Refresh UI │
└─────────────────┘
```
### ETag-Based Sync
The app uses ETags to efficiently detect changes:
1. Store ETag from last sync
2. Send ETag in request header
3. If server returns 304 (Not Modified), skip sync
4. If server returns new data, update local cache and ETag
### Remote Asset Removal
When assets are deleted on server:
1. Receive list of deleted asset IDs
2. Remove from local database
3. Update timeline display
4. Keep local-only assets intact (if any)
## WebSocket Events for Background Sync
Real-time events that trigger sync operations:
| Event | Action |
|-------|--------|
| `on_upload_success` | Add new asset to timeline |
| `on_asset_delete` | Remove asset from local DB |
| `on_asset_trash` | Move asset to trash |
| `on_asset_restore` | Restore asset from trash |
| `on_asset_update` | Refresh asset metadata |
| `on_asset_hidden` | Remove from visible timeline |
## Foreground Upload Service
For uploads while app is visible:
### Features
- Shows upload progress in UI
- Can be paused/cancelled by user
- Uses foreground service notification
- Continues if user switches apps briefly
### Progress Tracking
```
Upload State:
- totalCount: Total files to upload
- completedCount: Successfully uploaded
- failedCount: Failed uploads
- currentFile: File being uploaded
- currentProgress: 0-100 percentage
```
## App Lifecycle Integration
### On App Resume
1. Reconnect WebSocket
2. Refresh authentication
3. Trigger quick sync
4. Resume any pending uploads
### On App Pause
1. Start background service (if enabled)
2. Save current state
3. Disconnect WebSocket (after delay)
### On App Terminate
1. Complete current upload (if possible)
2. Schedule background task
3. Clean up resources
## Battery Optimization
The background service is designed to be battery-efficient:
- **Batching**: Groups multiple uploads
- **Debouncing**: Waits before processing WebSocket events (5 second debounce)
- **Conditions**: Respects WiFi and charging requirements
- **Scheduling**: Uses system job scheduler when possible
## Debug Logging
Enable advanced troubleshooting in settings to see detailed logs:
| Log Level | What's Logged |
|-----------|---------------|
| INFO | Service start/stop, sync complete |
| WARNING | Retry attempts, partial failures |
| SEVERE | Errors, failed uploads |
---
[Previous: Activities](activities.md) | [Next: WebSocket](websocket.md)

308
docs/data-models.md Normal file
View File

@@ -0,0 +1,308 @@
# Data Models
Core data structures used throughout the application.
## Asset
The fundamental unit representing a photo or video.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | Integer | Local database ID (auto-increment) |
| remoteId | String? | Server-side UUID |
| localId | String? | Device media library ID |
| checksum | String | SHA1 hash (base64 encoded) |
| thumbhash | String? | Compact image placeholder hash |
| ownerId | Integer | Hash of owner's user ID |
| fileCreatedAt | DateTime | When file was originally created |
| fileModifiedAt | DateTime | When file was last modified |
| updatedAt | DateTime | Last server update timestamp |
| durationInSeconds | Integer | Video duration (0 for images) |
| type | AssetType | image, video, audio, or other |
| width | Integer? | Image/video width in pixels |
| height | Integer? | Image/video height in pixels |
| fileName | String | Original filename |
| livePhotoVideoId | String? | ID of video component for live photos |
| isFavorite | Boolean | Marked as favorite |
| isArchived | Boolean | Hidden from main timeline |
| isTrashed | Boolean | In trash (pending deletion) |
| isOffline | Boolean | Server asset not accessible |
| stackId | String? | ID of stack this asset belongs to |
| stackPrimaryAssetId | String? | Primary asset in the stack |
| stackCount | Integer | Number of assets in stack |
| visibility | Enum | timeline, archive, hidden, locked |
### Asset Types
```
AssetType:
- other: Unknown file type
- image: Photo (JPEG, PNG, HEIC, etc.)
- video: Video file (MP4, MOV, etc.)
- audio: Audio file
```
### Asset State
Describes where asset data comes from:
```
AssetState:
- local: Only exists on device
- remote: Only exists on server
- merged: Exists on both (synced)
```
### Asset Visibility
```
AssetVisibilityEnum:
- timeline: Visible in main timeline
- archive: In archive (hidden from timeline)
- hidden: Hidden (private)
- locked: In locked folder (PIN protected)
```
### Computed Properties
| Property | Description |
|----------|-------------|
| isLocal | Has local device ID |
| isRemote | Has server ID |
| isImage | Type is image |
| isVideo | Type is video |
| isMotionPhoto | Has live photo video component |
| aspectRatio | Width / height ratio |
| duration | Duration as time object |
| name | Filename without extension |
| storage | Current asset state |
| isFlipped | EXIF indicates rotation 90/270 |
| orientatedWidth | Width accounting for rotation |
| orientatedHeight | Height accounting for rotation |
## Album
Collection of assets.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | Integer | Local database ID |
| remoteId | String? | Server-side UUID |
| name | String | Album name |
| createdAt | DateTime | Creation timestamp |
| modifiedAt | DateTime | Last modification |
| startDate | DateTime? | Earliest asset date |
| endDate | DateTime? | Latest asset date |
| lastModifiedAssetTimestamp | DateTime? | When assets last changed |
| shared | Boolean | Is shared with others |
| ownerId | Integer | Owner's user ID hash |
| owner | User | Owner user object |
| activityEnabled | Boolean | Comments/likes allowed |
| isRemote | Boolean | Exists on server |
| assetCount | Integer | Number of assets |
### Album Types
- **Regular Album**: User-created collection
- **Shared Album**: Shared with other users
- **Partner Album**: Virtual album showing partner's assets
## User
Represents an app user.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | String | Unique user UUID |
| email | String | User's email address |
| name | String | Display name |
| profileImagePath | String? | URL to profile picture |
| isAdmin | Boolean | Has admin privileges |
| memoryEnabled | Boolean | Show memories feature |
| avatarColor | Color | Generated avatar color |
| quotaUsageInBytes | Integer | Storage used |
| quotaSizeInBytes | Integer? | Storage quota (null = unlimited) |
| inTimeline | Boolean | Show in partner timeline |
| isPartnerSharedBy | Boolean | Sharing with current user |
| isPartnerSharedWith | Boolean | Current user shares with them |
## ExifInfo
Metadata extracted from images.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| assetId | Integer | Associated asset ID |
| make | String? | Camera manufacturer |
| model | String? | Camera model |
| lens | String? | Lens used |
| fNumber | Float? | Aperture f-stop |
| focalLength | Float? | Focal length in mm |
| iso | Integer? | ISO sensitivity |
| exposureTime | String? | Shutter speed |
| latitude | Float? | GPS latitude |
| longitude | Float? | GPS longitude |
| city | String? | Location city |
| state | String? | Location state/province |
| country | String? | Location country |
| description | String? | Image description/caption |
| dateTimeOriginal | DateTime? | Original capture time |
### Computed Properties
| Property | Description |
|----------|-------------|
| hasCoordinates | Has valid GPS data |
| isFlipped | Orientation is 90 or 270 degrees |
## Activity
Comment or like on shared album.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | String | Activity UUID |
| assetId | String? | Specific asset (null = album-level) |
| comment | String? | Comment text (null for likes) |
| createdAt | DateTime | When activity was created |
| type | ActivityType | comment or like |
| user | User | User who created activity |
### Activity Types
```
ActivityType:
- comment: Text comment
- like: Thumbs up reaction
```
## Search Filters
Parameters for searching assets.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| context | String? | Text to search |
| filename | String? | Search by filename |
| personIds | List<String> | Filter by people |
| location | SearchLocationFilter? | City/state/country |
| camera | SearchCameraFilter? | Make/model |
| date | SearchDateFilter? | Date range |
| display | SearchDisplayFilters? | Archive/favorite/trash |
| mediaType | AssetType? | Image/video filter |
### Location Filter
```
SearchLocationFilter:
- city: String?
- state: String?
- country: String?
```
### Camera Filter
```
SearchCameraFilter:
- make: String?
- model: String?
```
### Date Filter
```
SearchDateFilter:
- takenAfter: DateTime?
- takenBefore: DateTime?
```
### Display Filters
```
SearchDisplayFilters:
- isArchived: Boolean?
- isFavorite: Boolean?
- isNotInAlbum: Boolean?
```
## Memory
"On this day" memories feature.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | String | Memory UUID |
| title | String | Display title |
| assets | List<Asset> | Assets in this memory |
| yearsAgo | Integer | How many years ago |
## Backup Album
Local album selected for backup.
### Properties
| Property | Type | Description |
|----------|------|-------------|
| id | String | Device album ID |
| name | String | Album name |
| selection | BackupSelection | include, exclude, or none |
| lastBackup | DateTime? | Last successful backup time |
## Store Keys
Key-value storage for app state and settings.
### Key Categories
**Authentication:**
- serverUrl, serverEndpoint
- accessToken
- currentUser
**Backup:**
- autoBackup, backgroundBackup
- backupRequireWifi, backupRequireCharging
- backupTriggerDelay
- backupFailedSince
**Display:**
- themeMode, primaryColor
- dynamicTheme, colorfulInterface
- tilesPerRow, dynamicLayout
- groupAssetsBy
**Cache:**
- thumbnailCacheSize
- imageCacheSize
- albumThumbnailCacheSize
**Features:**
- syncAlbums
- betaTimeline
- enableHapticFeedback
**Map:**
- mapThemeMode
- mapShowFavoriteOnly
- mapIncludeArchived
- mapwithPartners
- mapRelativeDate
---
[Previous: WebSocket](websocket.md) | [Next: API Reference](api-reference.md)

344
docs/error-handling.md Normal file
View File

@@ -0,0 +1,344 @@
# Error Handling
The app implements comprehensive error handling to provide a robust user experience.
## Error Architecture
```
┌─────────────────────────────────────────┐
│ UI Layer │
│ (Shows error messages to user) │
└─────────────────┬───────────────────────┘
┌─────────────────▼───────────────────────┐
│ State Management │
│ (Catches errors, updates state) │
└─────────────────┬───────────────────────┘
┌─────────────────▼───────────────────────┐
│ Service Layer │
│ (Logs errors, returns defaults) │
└─────────────────┬───────────────────────┘
┌─────────────────▼───────────────────────┐
│ Repository Layer │
│ (Handles API/DB errors) │
└─────────────────────────────────────────┘
```
## Error Logger Mixin
Services use a standardized error handling pattern:
### Guard Error
Wraps async operations and returns result or error:
```
Input: Async function
Output: AsyncValue<T>
- AsyncData(result) on success
- AsyncError(error, stackTrace) on failure
Behavior:
1. Execute async function
2. If success → return AsyncData with result
3. If exception → log error, return AsyncError
```
### Log Error
Executes operation and returns default on failure:
```
Input:
- Async function
- Default value
- Error message
Output: T (result or default)
Behavior:
1. Execute async function
2. If success → return result
3. If exception → log error, return default value
```
## Log Levels
| Level | Value | Usage |
|-------|-------|-------|
| ALL | 0 | All messages |
| FINEST | 300 | Extremely detailed |
| FINER | 400 | Very detailed |
| FINE | 500 | Detailed tracing |
| CONFIG | 700 | Configuration info |
| INFO | 800 | General information |
| WARNING | 900 | Potential problems |
| SEVERE | 1000 | Serious failures |
| SHOUT | 1200 | Critical errors |
| OFF | 2000 | No logging |
Default level: INFO (800)
## API Error Handling
### HTTP Error Responses
| Status Code | Meaning | Handling |
|-------------|---------|----------|
| 400 | Bad Request | Show validation error |
| 401 | Unauthorized | Redirect to login |
| 403 | Forbidden | Show access denied |
| 404 | Not Found | Show not found message |
| 409 | Conflict | Handle duplicate |
| 500 | Server Error | Show generic error |
### Network Errors
| Error Type | Handling |
|------------|----------|
| No Connection | Show offline message |
| Timeout | Retry with backoff |
| SSL Error | Check certificate settings |
| DNS Failure | Check server URL |
### Error Response Format
```json
{
"error": "ErrorType",
"statusCode": 400,
"message": "Human-readable error message"
}
```
## Authentication Errors
### Login Failures
| Error | Message | Action |
|-------|---------|--------|
| Wrong credentials | "Incorrect email or password" | Show error, stay on login |
| Account locked | "Account temporarily locked" | Show lockout message |
| Server unavailable | "Cannot connect to server" | Check server URL |
### Session Expiration
1. API returns 401
2. Clear local token
3. Navigate to login
4. Show "Session expired" message
### OAuth Errors
| Error | Handling |
|-------|----------|
| User cancelled | Return to login silently |
| Invalid state | Show security error |
| Token exchange failed | Show error, retry option |
## Upload Errors
### Upload Failure Handling
```
Upload Attempt
Success?
│ │
Yes No
│ │
▼ ▼
Done Retry?
│ │
Yes No (max retries)
│ │
▼ ▼
Retry Mark Failed
Add to Error Queue
Grace Period Check
┌────┴────┐
│ │
Within Exceeded
│ │
▼ ▼
Silent Show Notification
```
### Error Grace Period
- Don't immediately notify on upload failures
- Wait for `uploadErrorNotificationGracePeriod` failures
- Then show notification
- Prevents spam for transient errors
### Duplicate Detection
When server returns duplicate error:
1. Mark local asset as backed up
2. Don't show error to user
3. Continue with next asset
## Download Errors
| Error | Handling |
|-------|----------|
| Asset not found | Show "Photo not available" |
| Permission denied | Show access error |
| Storage full | Show storage warning |
| Network error | Offer retry |
## Sync Errors
### Sync Failure Recovery
1. Log sync error
2. Keep existing local data
3. Schedule retry
4. Show sync indicator if prolonged
### Conflict Resolution
When local and remote conflict:
1. Remote data takes precedence
2. Merge where possible
3. Log conflict details
## UI Error Display
### Snackbar Messages
For transient errors:
```
┌────────────────────────────────────┐
│ Error message here [RETRY]│
└────────────────────────────────────┘
```
- Auto-dismiss after 3-5 seconds
- Optional action button
- Non-blocking
### Dialog Messages
For critical errors requiring action:
```
┌─────────────────────────────────┐
│ Error Title │
├─────────────────────────────────┤
│ │
│ Detailed error description │
│ with helpful guidance. │
│ │
├─────────────────────────────────┤
│ [OK] [RETRY] │
└─────────────────────────────────┘
```
### Inline Errors
For form validation:
```
┌─────────────────────────────────┐
│ Email │
│ ┌───────────────────────────┐ │
│ │ invalid-email │ │
│ └───────────────────────────┘ │
│ ⚠ Please enter a valid email │
└─────────────────────────────────┘
```
### Empty States
When no data due to error:
```
┌─────────────────────────────────┐
│ │
│ [Error Icon] │
│ │
│ Something went wrong │
│ │
│ [Try Again] │
│ │
└─────────────────────────────────┘
```
## Localization Keys
Common error message keys:
| Key | Usage |
|-----|-------|
| `error_generic` | Unknown error occurred |
| `error_network` | Network connection error |
| `error_server` | Server error |
| `error_unauthorized` | Not authorized |
| `error_not_found` | Resource not found |
| `error_upload_failed` | Upload failed |
| `error_download_failed` | Download failed |
## Debug Logging
### Enable Advanced Troubleshooting
When enabled in settings:
- Log all API requests/responses
- Log state changes
- Log error stack traces
- Log sync operations
### Log Output
Logs include:
- Timestamp
- Logger name (component)
- Log level
- Message
- Error object (if applicable)
- Stack trace (if applicable)
### Viewing Logs
- Settings → Advanced → Export Logs
- Share log file for support
## Error Recovery Patterns
### Automatic Retry
For transient failures:
```
Attempt 1
Fail → Wait 1s → Attempt 2
Fail → Wait 2s → Attempt 3
Fail → Give Up
```
### Manual Retry
User-triggered retry:
1. Show error with retry button
2. User taps retry
3. Clear error state
4. Repeat operation
### Graceful Degradation
When feature unavailable:
1. Disable affected UI elements
2. Show explanation
3. Continue with available features
---
[Previous: Settings](settings.md) | [Next: UI Components](ui-components.md)

326
docs/local-storage.md Normal file
View File

@@ -0,0 +1,326 @@
# Local Storage
The app uses multiple storage mechanisms to persist data locally for offline access and performance.
## Storage Overview
```
┌─────────────────────────────────────────┐
│ Local Storage │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Key-Value │ │ Database │ │
│ │ Store │ │ (Isar) │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Image │ │ Secure │ │
│ │ Cache │ │ Storage │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────┘
```
## Key-Value Store
Simple key-value storage for settings and small data.
### Store Service
The Store service provides typed access to stored values:
```
// Get a value with default
value = Store.get(StoreKey.themeMode, "system")
// Get a value (nullable)
value = Store.tryGet(StoreKey.accessToken)
// Save a value
Store.put(StoreKey.themeMode, "dark")
// Delete a value
Store.delete(StoreKey.accessToken)
```
### Store Keys
| Key | Type | Description |
|-----|------|-------------|
| version | Integer | Store schema version |
| assetETag | String | Last sync ETag |
| currentUser | User | Logged in user object |
| deviceId | String | Unique device identifier |
| deviceIdHash | Integer | Hashed device ID |
| serverUrl | String | Server URL |
| serverEndpoint | String | API endpoint URL |
| accessToken | String | Authentication token |
| autoBackup | Boolean | Auto backup enabled |
| backgroundBackup | Boolean | Background backup enabled |
| backupRequireWifi | Boolean | Require WiFi for backup |
| backupRequireCharging | Boolean | Require charging for backup |
| backupTriggerDelay | Integer | Minutes before backup starts |
| backupFailedSince | DateTime | When backup started failing |
### User Settings Keys
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| loadPreview | Boolean | true | Load preview images |
| loadOriginal | Boolean | false | Load full resolution |
| themeMode | String | "system" | light/dark/system |
| primaryColor | String | Default | Theme accent color |
| dynamicTheme | Boolean | false | Use dynamic colors |
| colorfulInterface | Boolean | true | Colorful UI elements |
| tilesPerRow | Integer | 4 | Grid columns |
| dynamicLayout | Boolean | false | Dynamic grid layout |
| groupAssetsBy | Integer | 0 | Grouping mode |
| storageIndicator | Boolean | true | Show storage indicator |
| loopVideo | Boolean | true | Loop video playback |
| autoPlayVideo | Boolean | true | Auto-play videos |
| enableHapticFeedback | Boolean | true | Haptic feedback |
### Cache Size Keys
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| thumbnailCacheSize | Integer | 10000 | Max thumbnail cache entries |
| imageCacheSize | Integer | 350 | Max preview cache entries |
| albumThumbnailCacheSize | Integer | 200 | Max album thumbnail entries |
## Database (Isar)
Embedded NoSQL database for structured data.
### Collections
#### Assets Collection
Stores all asset metadata:
```
Assets:
- id: Auto-increment primary key
- remoteId: Indexed (unique hash)
- localId: Indexed (unique hash)
- ownerId + checksum: Composite unique index
- All asset properties
```
**Indexes:**
- `remoteId` - Fast lookup by server ID
- `localId` - Fast lookup by device ID
- `ownerId, checksum` - Composite unique constraint
#### Albums Collection
Stores album information:
```
Albums:
- id: Auto-increment primary key
- remoteId: Indexed
- name, dates, ownership info
- Links to assets and users
```
#### Backup Albums Collection
Tracks device albums selected for backup:
```
BackupAlbum:
- id: Device album ID (primary key)
- name: Album name
- selection: include/exclude/none
- lastBackup: Last backup timestamp
```
#### Duplicated Assets Collection
Tracks already-uploaded assets:
```
DuplicatedAsset:
- id: Device asset ID
- Prevents re-upload of same file
```
#### ETag Collection
Stores sync ETags for incremental updates:
```
ETag:
- id: User ID
- assetCount: Number of assets
- time: Last sync time
```
#### Device Asset Collections
Platform-specific asset tracking:
**Android:**
```
AndroidDeviceAsset:
- id: Asset ID
- hash: Content hash
```
**iOS:**
```
IOSDeviceAsset:
- id: Asset ID
- hash: Content hash
- modifiedTime: Modification timestamp
```
### Database Operations
**Read Operations:**
```
// Find asset by remote ID
asset = db.assets.where().remoteIdEqualTo(id).findFirst()
// Get all assets for user
assets = db.assets.filter().ownerIdEqualTo(userId).findAll()
// Get assets in date range
assets = db.assets.filter()
.fileCreatedAtBetween(start, end)
.findAll()
```
**Write Operations:**
```
// Insert/update asset
db.writeTxn(() => db.assets.put(asset))
// Delete by remote IDs
db.writeTxn(() => db.assets.deleteAllByRemoteId(ids))
// Batch insert
db.writeTxn(() => db.assets.putAll(assets))
```
## Image Cache
Caches downloaded images for performance.
### Cache Layers
```
┌─────────────────┐
│ Memory Cache │ ← Hot images (limited size)
├─────────────────┤
│ Disk Cache │ ← Persistent storage
└─────────────────┘
```
### Cache Types
| Type | Purpose | Default Size |
|------|---------|--------------|
| Thumbnail | Grid view thumbnails | 10,000 entries |
| Preview | Full-screen previews | 350 entries |
| Album Thumbnail | Album cover images | 200 entries |
### Cache Management
**Clear All Cache:**
- Remove all cached thumbnails
- Remove all cached previews
- Free up storage space
**Cache Invalidation:**
- By asset ID when asset is deleted
- By album ID when album changes
- Manual clear from settings
## Secure Storage
For sensitive data that needs encryption.
### Stored Securely
| Data | Usage |
|------|-------|
| Access Token | Authentication |
| SSL Client Certificate | mTLS authentication |
| SSL Certificate Password | Certificate decryption |
| Custom Headers | Proxy authentication |
### SSL Client Certificate Storage
```
SSLClientCertStoreVal:
- data: Certificate binary (base64 encoded)
- password: Certificate password
Operations:
- save(): Persist to secure storage
- load(): Retrieve from secure storage
- delete(): Remove from secure storage
```
## Storage Location
### Per-Platform Paths
**Android:**
- Database: `/data/data/{app}/databases/`
- Cache: `/data/data/{app}/cache/`
- Secure: Android Keystore
**iOS:**
- Database: `~/Library/Application Support/`
- Cache: `~/Library/Caches/`
- Secure: iOS Keychain
## Data Migration
When upgrading the app, data may need migration:
### Version Tracking
- Store version in `StoreKey.version`
- Compare with expected version
- Run migration steps if needed
### Migration Steps
1. Read current version
2. Apply migrations sequentially
3. Update version number
4. Verify data integrity
## Cleanup Operations
### Free Up Space
Remove local copies of backed-up assets:
**Settings:**
- `cleanupKeepFavorites`: Keep favorite assets
- `cleanupKeepMediaType`: Keep photos/videos/both
- `cleanupKeepAlbumIds`: Keep assets in specific albums
- `cleanupCutoffDaysAgo`: Only remove older than X days
### Clear Cache
Remove cached images without affecting synced data:
- Thumbnails
- Previews
- Album covers
### Full Reset
Remove all local data:
1. Clear database
2. Clear cache
3. Clear settings
4. Return to login screen
---
[Previous: API Reference](api-reference.md) | [Next: Settings](settings.md)

326
docs/settings.md Normal file
View File

@@ -0,0 +1,326 @@
# Settings
The app provides extensive customization through the settings page.
## Settings Structure
```
Settings
├── Preferences
├── Timeline Display
├── Asset Viewer
├── Backup
├── Free Up Space
├── Networking
├── Notifications
├── Language
├── Advanced
└── Sync Status (Beta)
```
## Settings Page Layout
**Mobile Layout:**
List of settings cards, each navigates to a sub-page.
**Tablet Layout:**
Split view with category list on left, selected settings on right.
```
┌──────────────────────────────────────────┐
│ Settings │
├──────────────┬───────────────────────────┤
│ │ │
│ Preferences │ [Selected Category │
│ Timeline │ Settings Content] │
│ Asset Viewer │ │
│ Backup │ │
│ ... │ │
│ │ │
└──────────────┴───────────────────────────┘
```
## Preferences
General app behavior settings.
### Theme Settings
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Theme Mode | Light / Dark / System | System | App color scheme |
| Primary Color | Color presets | Default | Accent color |
| Dynamic Theme | On / Off | Off | Use system dynamic colors |
| Colorful Interface | On / Off | On | Apply color to more UI elements |
### Interaction Settings
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Haptic Feedback | On / Off | On | Vibration on interactions |
### Read-Only Mode
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Enable Read-Only | On / Off | Off | Disable modifications |
When enabled:
- Cannot edit assets
- Cannot delete assets
- Cannot create albums
- Can still browse and view
## Timeline Display
Customize how photos appear in the timeline.
### Grid Layout
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Tiles Per Row | 2-6 | 4 | Number of columns |
| Dynamic Layout | On / Off | Off | Variable tile sizes |
| Show Storage Indicator | On / Off | On | Show local/remote icon |
### Grouping
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Group Assets By | None / Day / Month | Day | How to group photos |
### Album Sort
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Album Sort Order | Title / Created / Modified | Modified | Sort albums by |
| Sort Direction | Ascending / Descending | Descending | Sort order |
| Album View | List / Grid | List | Album display style |
## Asset Viewer
Settings for the full-screen photo/video viewer.
### Image Loading
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Load Preview | On / Off | On | Load preview quality |
| Load Original | On / Off | Off | Load full resolution |
| Prefer Remote | On / Off | Off | Prefer server image |
### Video Settings
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Auto Play | On / Off | On | Play video automatically |
| Loop Video | On / Off | On | Repeat video |
| Load Original Video | On / Off | Off | Load full quality video |
### Navigation
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Tap to Navigate | On / Off | Off | Tap sides to go prev/next |
## Backup Settings
Control automatic photo backup.
### Foreground Backup
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Enable Backup | On / Off | Off | Upload photos to server |
| Use Cellular (Photos) | On / Off | Off | Upload photos on cellular |
| Use Cellular (Videos) | On / Off | Off | Upload videos on cellular |
### Background Backup
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Background Backup | On / Off | Off | Upload when app closed |
| Require WiFi | On / Off | On | Only on WiFi |
| Require Charging | On / Off | Off | Only when charging |
| Trigger Delay | Minutes | 30 | Wait before starting |
### Notification Settings
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Total Progress | On / Off | On | Show overall progress |
| Single Progress | On / Off | Off | Show per-file progress |
| Error Grace Period | Number | 2 | Errors before notification |
### Album Selection
Select which device albums to back up:
```
┌─────────────────────────────────┐
│ Backup Albums │
├─────────────────────────────────┤
│ [✓] Camera Roll │
│ [✓] Screenshots │
│ [ ] Downloads │
│ [ ] WhatsApp Images │
│ [] Excluded Album │
└─────────────────────────────────┘
[✓] = Include in backup
[ ] = Not selected
[] = Explicitly excluded
```
## Free Up Space
Remove local copies of backed-up assets.
### Filter Options
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Keep Favorites | On / Off | On | Don't remove favorites |
| Keep Media Type | All / Photos / Videos | All | Which to keep |
| Keep in Albums | Album list | None | Keep assets in albums |
| Cutoff Days | Number | All | Only remove older than |
### Cleanup Process
1. Calculate removable assets
2. Show preview of space to be freed
3. Confirm with user
4. Remove local files
5. Keep server copies intact
## Networking
Network and connection settings.
### Server Configuration
| Setting | Description |
|---------|-------------|
| Server URL | Primary server address |
| Local Endpoint | LAN server address |
| Preferred WiFi | WiFi name for local endpoint |
| External Endpoints | List of external addresses |
### Auto Endpoint Switching
When enabled:
1. Check current WiFi name
2. If matches preferred WiFi → use local endpoint
3. Otherwise → use external endpoint
4. Fall back on connection failure
### SSL/TLS Settings
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Allow Self-Signed | On / Off | Off | Accept self-signed certs |
| Client Certificate | File | None | mTLS client certificate |
### Custom Headers
Add custom HTTP headers for proxy authentication:
```
Header Name: X-Custom-Auth
Header Value: token123
```
## Notifications
Control app notifications.
| Setting | Description |
|---------|-------------|
| Backup Progress | Show backup progress notifications |
| Backup Complete | Notify when backup finishes |
| Backup Errors | Notify on upload failures |
## Language
Change app display language.
- System default
- Manual language selection
- List of supported languages
## Advanced
Developer and troubleshooting options.
### Troubleshooting
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Advanced Logging | On / Off | Off | Detailed debug logs |
| Log Level | Info-Severe | Info | Minimum log level |
### Platform-Specific
**Android:**
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Manage Local Media | On / Off | Off | Request full media access |
| Photo Manager Filter | On / Off | On | Custom media filtering |
**iOS:**
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Ignore iCloud Assets | On / Off | Off | Skip iCloud-only photos |
### Experimental
| Setting | Options | Default | Description |
|---------|---------|---------|-------------|
| Beta Timeline | On / Off | On | Use new timeline sync |
| Sync Albums | On / Off | Off | Sync device albums |
### Cache Management
- Clear thumbnail cache
- Clear preview cache
- View cache size
- Reset all caches
### Data Management
- Export logs
- Reset settings
- Clear local data
- Sign out and clear
## Sync Status (Beta)
When beta timeline is enabled, shows sync status:
### Status Information
- Last sync time
- Sync state (idle/syncing/error)
- Number of synced assets
- Pending changes
### Actions
- Force full sync
- Reset sync state
- View sync logs
## Settings Persistence
Settings are saved to local storage immediately on change:
1. User changes setting
2. Update in-memory state
3. Persist to Store
4. Reflect in UI
Settings sync is local-only - each device maintains its own settings.
---
[Previous: Local Storage](local-storage.md) | [Next: Error Handling](error-handling.md)

331
docs/theming.md Normal file
View File

@@ -0,0 +1,331 @@
# Theming
The app supports comprehensive theming to match user preferences and system settings.
## Theme Structure
```
Theme
├── Color Scheme (light/dark)
├── Typography
├── Component Themes
└── Custom Extensions
```
## Color Schemes
### Light Theme Colors
| Color Role | Usage |
|------------|-------|
| primary | Main accent color, active elements |
| onPrimary | Text/icons on primary color |
| surface | Background color |
| onSurface | Text on surface |
| surfaceContainer | Card backgrounds |
| surfaceContainerHigh | Elevated surfaces |
| surfaceContainerHighest | Highest elevation |
| outline | Borders, dividers |
| error | Error states |
### Dark Theme Colors
Same roles as light theme but with dark-appropriate values:
- Surface colors are dark grays
- Primary colors may be adjusted for contrast
- Text colors are light
### Surface Color Scale
```
Light Mode:
surfaceContainerLowest → #FFFFFF (white)
surfaceContainerLow → #F3F3F3
surfaceContainer → #EEEEEE
surfaceContainerHigh → #E8E8E8
surfaceContainerHighest → #E2E2E2
surface → #F9F9F9
surfaceDim → #DADADA
surfaceBright → #F9F9F9
Dark Mode:
surfaceContainerLowest → #0E0E0E
surfaceContainerLow → #1B1B1B
surfaceContainer → #1F1F1F
surfaceContainerHigh → #242424
surfaceContainerHighest → #2E2E2E
surface → #131313
surfaceDim → #131313
surfaceBright → #353535
```
## Theme Modes
### System (Default)
Follows device system setting:
- Automatically switches with system
- Respects scheduled dark mode
### Light Mode
Always uses light theme:
- Bright backgrounds
- Dark text
- Suitable for outdoor use
### Dark Mode
Always uses dark theme:
- Dark backgrounds
- Light text
- Reduces eye strain at night
- Saves battery on OLED screens
## Primary Color Options
The app offers color presets:
| Name | Primary Color | Usage |
|------|---------------|-------|
| Default | Blue | Standard app color |
| Red | #F44336 | Bold accent |
| Green | #4CAF50 | Nature theme |
| Purple | #9C27B0 | Elegant |
| Orange | #FF9800 | Warm |
| Pink | #E91E63 | Playful |
| Teal | #009688 | Cool |
| Custom | User-defined | Any color |
## Dynamic Theme
When enabled (and supported by platform):
1. Extract colors from wallpaper
2. Generate harmonious palette
3. Apply as app theme
**Platform Support:**
- Android 12+ (Material You)
- iOS (limited support)
## Colorful Interface
### Enabled (Default)
Primary color applied to:
- App bar titles
- Active navigation items
- Buttons
- Links
- Accent elements
### Disabled
More subtle appearance:
- Neutral app bars
- Reduced color usage
- Focus on content
## Typography
### Font Family
Default: "GoogleSans" (or system font for unsupported locales)
**Unsupported Locales:**
Languages that need special character support fall back to system fonts.
### Text Styles
| Style | Size | Weight | Usage |
|-------|------|--------|-------|
| displayLarge | 18 | SemiBold | Large headings |
| displayMedium | 14 | SemiBold | Medium headings |
| displaySmall | 12 | SemiBold | Small headings |
| titleLarge | 26 | SemiBold | Page titles |
| titleMedium | 18 | SemiBold | Section titles |
| titleSmall | 16 | SemiBold | Card titles |
| bodyLarge | 16 | Normal | Main text |
| bodyMedium | 14 | Normal | Body text |
| bodySmall | 12 | Normal | Captions |
## Component Themes
### App Bar
```
AppBarTheme:
- titleTextStyle: Primary color, 18px, SemiBold
- backgroundColor: Surface color
- foregroundColor: Primary color
- elevation: 0 (flat)
- centerTitle: true
```
### Navigation Bar
```
NavigationBarTheme:
- backgroundColor: Surface container (dark) or surface (light)
- labelTextStyle: 14px, Medium weight
- indicatorColor: Primary with opacity
```
### Buttons
**Elevated Button:**
```
- backgroundColor: Primary color
- foregroundColor: White (light) or Black (dark)
- shape: Rounded rectangle
```
### Input Fields
```
InputDecorationTheme:
- border: Rounded (15px radius)
- focusedBorder: Primary color
- enabledBorder: Outline variant color
- labelStyle: Primary color
```
### Cards/Dialogs
```
- backgroundColor: Surface container
- shape: Rounded corners (10px)
- elevation: Subtle shadow
```
### Chips
```
ChipTheme:
- side: No border
- shape: Stadium (pill shape)
```
### Progress Indicators
```
ProgressIndicatorTheme:
- Circular: Gap at 3px
- Linear: Rounded track
```
### Bottom Sheet
```
BottomSheetTheme:
- backgroundColor: Surface container
- shape: Rounded top corners
```
### Snackbar
```
SnackBarTheme:
- backgroundColor: Surface container highest
- contentTextStyle: Primary color, bold
```
## Custom Theme Extensions
### Secondary Surface Color
Additional surface variant for secondary text:
```
onSurfaceSecondary: Slightly faded text color
```
Used for:
- Timestamps
- Secondary labels
- Placeholder text
## Decolorized Surfaces
To prevent primary color from tinting surfaces:
The app uses static surface colors instead of seed-generated ones. This ensures:
- Neutral backgrounds
- Consistent appearance
- No unexpected color shifts
## Theme Application
### Build Theme Data
```
Inputs:
- ColorScheme (light or dark)
- Locale (for font selection)
Process:
1. Select base color scheme
2. Apply typography with locale font
3. Configure component themes
4. Add custom extensions
Output:
Complete theme data for the app
```
### Theme Switching
```
User changes theme setting
Store new preference
Rebuild theme data
Notify UI to rebuild
App repaints with new theme
```
## Accessibility
### Contrast Ratios
Theme colors maintain WCAG contrast ratios:
- Normal text: 4.5:1 minimum
- Large text: 3:1 minimum
### Color Independence
UI doesn't rely solely on color:
- Icons accompany colors
- Text labels included
- Patterns/shapes differentiate
### System Font Scaling
Typography respects system text size settings.
## Platform Considerations
### Android
- Material 3 design language
- Dynamic color extraction (12+)
- Edge-to-edge content
- System navigation theming
### iOS
- Cupertino adaptations where needed
- System font for locales
- Safe area handling
- Native-feeling interactions
---
[Previous: UI Components](ui-components.md) | [Back to Index](README.md)

444
docs/ui-components.md Normal file
View File

@@ -0,0 +1,444 @@
# UI Components
Reusable UI components used throughout the app.
## Asset Display Components
### Thumbnail Tile
Displays a single asset thumbnail in grids.
**Properties:**
- asset: Asset to display
- showStorageIndicator: Show local/remote badge
- onTap: Tap handler
- onLongPress: Long press handler (selection)
- isSelected: Selection state
**Visual States:**
```
Normal: Selected:
┌──────────┐ ┌──────────┐
│ │ │ ┌──────┐ │
│ [Image] │ │ │Image │✓│
│ │ │ └──────┘ │
│ [●] │ │ [●] │
└──────────┘ └──────────┘
└─ Storage └─ Checkmark
indicator overlay
```
**Storage Indicators:**
- Cloud icon: Remote only
- Device icon: Local only
- Cloud+Device: Both (merged)
**Overlay Badges:**
- Video duration (bottom left)
- Stack count (bottom right)
- Favorite heart (top right)
### Thumbnail Image
Loads and displays asset images with progressive loading.
**Loading States:**
1. Show thumbhash placeholder (blurred)
2. Load thumbnail quality
3. Optionally load preview quality
4. Optionally load original
**Caching:**
- Uses image cache
- Key: asset ID + quality level
- Respects cache size limits
### Asset Grid
Displays assets in a scrollable grid.
**Properties:**
- assets: List of assets
- tilesPerRow: Column count (2-6)
- onAssetTap: Individual tap handler
- onSelectModeChanged: Selection mode callback
- enableMultiSelect: Allow multi-selection
**Features:**
- Virtualized scrolling (only renders visible items)
- Grouped by date with section headers
- Pull-to-refresh support
- Drag to scroll with date indicator
### Draggable Scrollbar
Custom scrollbar with date indicator.
**Components:**
```
┌─────────────────────────┬─┐
│ │▲│
│ ├─┤
│ [Asset Grid] │ │
│ │█│← Thumb
│ │ │ ┌──────────┐
│ │ │ │ Jan 2024 │← Date bubble
│ │ │ └──────────┘
│ │ │
│ ├─┤
│ │▼│
└─────────────────────────┴─┘
```
**Behaviors:**
- Drag thumb to fast scroll
- Date bubble shows current position
- Auto-hide when not interacting
- Haptic feedback on date changes
## Navigation Components
### Bottom Navigation Bar
Main app navigation.
**Tabs:**
```
┌─────────────────────────────────────┐
│ [Photos] [Search] [Sharing] [Library] │
│ ● ○ ○ ○ │
└─────────────────────────────────────┘
```
**States:**
- Selected: Filled icon + label
- Unselected: Outline icon
### App Bar
Standard navigation header.
**Variants:**
Default:
```
┌─────────────────────────────────────┐
│ [←] Title [⋮] │
└─────────────────────────────────────┘
```
Selection mode:
```
┌─────────────────────────────────────┐
│ [✕] 3 selected [Share][⋮] │
└─────────────────────────────────────┘
```
### Tab Bar
Secondary navigation within screens.
```
┌─────────────────────────────────────┐
│ Albums │ Shared │ │
│ ────────── ────────────── │
└─────────────────────────────────────┘
```
## Form Components
### Text Input
Standard text field with decorations.
**States:**
- Enabled: Normal appearance
- Focused: Primary color border
- Error: Red border + error message
- Disabled: Grayed out
**Layout:**
```
┌─────────────────────────────────────┐
│ Label │
│ ┌───────────────────────────────┐ │
│ │ [Icon] Placeholder text [X] │ │
│ └───────────────────────────────┘ │
│ Helper or error text │
└─────────────────────────────────────┘
```
### Search Bar
Specialized input for search.
```
┌─────────────────────────────────────┐
│ ┌───────────────────────────────┐ │
│ │ [🔍] Search photos... [X] │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Features:**
- Search icon prefix
- Clear button when has text
- Debounced input for API calls
### Settings Tiles
Various settings input components.
**Switch Tile:**
```
┌─────────────────────────────────────┐
│ Setting Title [═══] │
│ Description text │
└─────────────────────────────────────┘
```
**Slider Tile:**
```
┌─────────────────────────────────────┐
│ Setting Title 4 │
│ ──────●──────────────────────── │
└─────────────────────────────────────┘
```
**Radio Tiles:**
```
┌─────────────────────────────────────┐
│ ○ Option 1 │
│ ● Option 2 (selected) │
│ ○ Option 3 │
└─────────────────────────────────────┘
```
**Button Tile:**
```
┌─────────────────────────────────────┐
│ [Icon] Action Title [>] │
│ Description │
└─────────────────────────────────────┘
```
## User Interface
### User Circle Avatar
Displays user profile picture or initials.
**Properties:**
- user: User object
- size: Diameter in pixels
**Rendering:**
1. If user has profile image → show image
2. Otherwise → show initials on colored background
3. Avatar color derived from user ID
**Sizes:**
- Small: 30px (comments, lists)
- Medium: 44px (activity tiles)
- Large: 80px (profile page)
### User List Tile
Displays user in a list.
```
┌─────────────────────────────────────┐
│ [Avatar] User Name │
│ user@email.com │
└─────────────────────────────────────┘
```
## Feedback Components
### Loading Indicators
**Circular Progress:**
- Indeterminate: Spinning animation
- Determinate: Shows percentage
**Linear Progress:**
```
┌─────────────────────────────────────┐
│ ████████░░░░░░░░░░░ 40% │
└─────────────────────────────────────┘
```
**Skeleton Loader:**
```
┌──────┐ ┌──────┐ ┌──────┐
│░░░░░░│ │░░░░░░│ │░░░░░░│
│░░░░░░│ │░░░░░░│ │░░░░░░│
└──────┘ └──────┘ └──────┘
```
### Snackbar
Temporary notification at bottom.
```
┌─────────────────────────────────────┐
│ Message text here [ACTION]│
└─────────────────────────────────────┘
```
**Types:**
- Info: Default colors
- Success: Green tint
- Error: Red tint
- Warning: Orange tint
### Dialog
Modal overlay for confirmations.
```
┌─────────────────────────────────────┐
│ │
│ ┌───────────────────────────────┐ │
│ │ Dialog Title │ │
│ ├───────────────────────────────┤ │
│ │ │ │
│ │ Dialog content goes here │ │
│ │ │ │
│ ├───────────────────────────────┤ │
│ │ [Cancel] [Confirm] │ │
│ └───────────────────────────────┘ │
│ │
└─────────────────────────────────────┘
(dimmed background)
```
### Bottom Sheet
Slides up from bottom.
```
┌─────────────────────────────────────┐
│ │
│ │
│ (main content) │
│ │
├─────────────────────────────────────┤
│ ─────── (drag handle) │
│ │
│ Bottom sheet content │
│ │
│ [Action 1] │
│ [Action 2] │
│ [Action 3] │
│ │
└─────────────────────────────────────┘
```
**Behaviors:**
- Drag handle to resize/dismiss
- Can expand to full screen
- Dismissible by dragging down
## Photo Viewer Components
### Top Control Bar
Overlay controls on asset viewer.
```
┌─────────────────────────────────────┐
│ [←] filename.jpg [♡] [⋮] │
└─────────────────────────────────────┘
```
**Actions:**
- Back: Return to grid
- Favorite: Toggle favorite
- More: Show action menu
### Bottom Control Bar
Additional actions for current asset.
```
┌─────────────────────────────────────┐
│ [Share] [Archive] [Delete] [More] │
└─────────────────────────────────────┘
```
### EXIF Panel
Shows metadata when expanded.
```
┌─────────────────────────────────────┐
│ Details │
├─────────────────────────────────────┤
│ 📅 January 15, 2024 3:30 PM │
│ 📷 iPhone 15 Pro │
│ 🔧 f/1.8 1/120s ISO 100 │
│ 📍 New York, United States │
└─────────────────────────────────────┘
```
## Map Components
### Map View
Displays assets on a map.
**Features:**
- Cluster markers for grouped assets
- Tap cluster to zoom
- Tap marker to show asset
- Map style (light/dark/satellite)
### Map Settings Sheet
Filter controls for map.
```
┌─────────────────────────────────────┐
│ Map Settings │
├─────────────────────────────────────┤
│ Show favorites only [═══] │
│ Include archived [═══] │
│ Include partner photos [═══] │
│ │
│ Time Range: │
│ [All Time ▼] │
└─────────────────────────────────────┘
```
## Album Components
### Album Thumbnail
Album preview with cover image.
```
┌─────────────────────────────────────┐
│ ┌───────────────────────────────┐ │
│ │ │ │
│ │ [Cover Image] │ │
│ │ │ │
│ └───────────────────────────────┘ │
│ Album Name │
│ 50 items │
└─────────────────────────────────────┘
```
### Album App Bar
Album-specific header with actions.
```
┌─────────────────────────────────────┐
│ [←] Album Name [Share] [⋮] │
└─────────────────────────────────────┘
```
---
[Previous: Error Handling](error-handling.md) | [Next: Theming](theming.md)

291
docs/websocket.md Normal file
View File

@@ -0,0 +1,291 @@
# WebSocket - Real-Time Updates
The app maintains a WebSocket connection to receive real-time updates from the server, enabling instant synchronization when changes occur.
## Connection Management
### Connection Setup
**WebSocket URL:** `{server-origin}/socket.io`
**Transport:** WebSocket (not polling)
**Options:**
- Auto-reconnection enabled
- Force new connection on each connect
- Custom WebSocket connector for SSL support
### Connection Lifecycle
```
┌─────────────────┐
│ User Logged In │
└────────┬────────┘
┌─────────────────┐
│ Build Socket │
│ with Auth │
└────────┬────────┘
┌─────────────────┐ Reconnect
│ Connect │◄───────────┐
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ onConnect │ │
│ - Set connected │ │
│ - Start listen │ │
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ │
│ Listen Events │ │
└────────┬────────┘ │
│ │
┌────┴────┐ │
│ │ │
▼ ▼ │
Event Error/ │
Received Disconnect │
│ │ │
▼ └────────────────┘
┌─────────────────┐
│ Process Event │
└─────────────────┘
```
### Connection States
| State | Description |
|-------|-------------|
| Disconnected | Not connected to server |
| Connected | Active WebSocket connection |
| Reconnecting | Attempting to restore connection |
## Event Types
### Asset Events
| Event Name | Payload | Action |
|------------|---------|--------|
| `on_upload_success` | Asset JSON | Add new asset to timeline |
| `on_asset_delete` | Asset ID | Remove asset from local DB |
| `on_asset_trash` | Asset IDs array | Move assets to trash |
| `on_asset_restore` | - | Refresh all assets |
| `on_asset_update` | - | Refresh asset metadata |
| `on_asset_stack_update` | - | Refresh stacked assets |
| `on_asset_hidden` | Asset ID | Remove from visible timeline |
### Server Events
| Event Name | Payload | Action |
|------------|---------|--------|
| `on_config_update` | - | Refresh server features and config |
| `on_new_release` | Version info | Show update notification |
### Beta Timeline Events
| Event Name | Payload | Action |
|------------|---------|--------|
| `AssetUploadReadyV1` | Asset data | Sync new upload to timeline |
| `AssetEditReadyV1` | Edit data | Sync asset edit |
## Pending Changes System
Events are batched to prevent UI thrashing:
### Change Types
```
PendingAction:
- assetDelete: Asset was deleted
- assetUploaded: New asset uploaded
- assetHidden: Asset hidden from timeline
- assetTrash: Asset moved to trash
```
### Processing Flow
```
┌─────────────────┐
│ Event Received │
└────────┬────────┘
┌─────────────────┐
│ Add to Pending │
│ Changes List │
└────────┬────────┘
┌─────────────────┐
│ Start Debounce │
│ Timer (500ms) │
└────────┬────────┘
More Events?
│ │
Yes No
│ │
▼ ▼
Reset ┌─────────────────┐
Timer │ Process Batch │
│ │ - Uploads │
└───►│ - Deletes │
│ - Hidden │
│ - Trash │
└─────────────────┘
```
### Debouncing
**Standard Debounce:** 500ms
- Waits 500ms after last event before processing
- Prevents rapid UI updates during bulk operations
**Batch Debounce:** 5 seconds (max 10 seconds)
- For upload events
- Batches multiple uploads together
- Maximum wait time of 10 seconds
## Event Handlers
### Upload Success Handler
When a new asset is uploaded (from web or another device):
1. Receive asset JSON data
2. Add to pending changes
3. After debounce:
- Parse asset response
- Create local Asset object
- Add to local database
- Update timeline display
### Asset Delete Handler
When an asset is permanently deleted:
1. Receive asset ID
2. Add to pending changes
3. After debounce:
- Remove from local database
- Update timeline display
### Asset Trash Handler
When assets are moved to trash:
1. Receive array of asset IDs
2. Add to pending changes
3. After debounce:
- Mark assets as trashed in local DB
- Remove from main timeline
- Refresh asset list
### Asset Hidden Handler
When an asset is hidden (moved to locked folder):
1. Receive asset ID
2. Add to pending changes
3. After debounce:
- Remove from local database
- Asset only visible in locked folder
### Config Update Handler
When server configuration changes:
1. Receive event (no payload)
2. Immediately refresh:
- Server features
- Server configuration
### Release Update Handler
When a new server version is available:
1. Receive version payload:
```json
{
"serverVersion": {"major": 1, "minor": 2, "patch": 3},
"releaseVersion": {"major": 1, "minor": 3, "patch": 0}
}
```
2. Parse version info
3. Update server info provider
4. Show update notification if applicable
## WebSocket State
```
WebsocketState:
- socket: The socket instance (or null if disconnected)
- isConnected: Boolean connection status
- pendingChanges: List of changes waiting to be processed
```
## Lifecycle Integration
### On App Start
1. Check if user is authenticated
2. If yes, connect WebSocket
3. Register event listeners
### On App Foreground
1. Reconnect if disconnected
2. Clear stale pending changes
### On App Background
1. Keep connection for brief period
2. Disconnect after timeout
3. Background service takes over
### On Logout
1. Disconnect WebSocket
2. Clear socket instance
3. Clear pending changes
## Error Handling
### Connection Errors
- Log error message
- Set state to disconnected
- Auto-reconnection handles recovery
### Event Processing Errors
- Log error with stack trace
- Continue processing other events
- Don't crash the app
## Switching Event Listeners
The app can switch between different event listener sets:
**Stop Old Events:**
```
- on_upload_success
- on_asset_delete
- on_asset_trash
- on_asset_restore
- on_asset_update
- on_asset_stack_update
- on_asset_hidden
```
**Start Beta Events:**
```
- AssetUploadReadyV1
- AssetEditReadyV1
```
This allows smooth transition between different sync implementations.
---
[Previous: Background Services](background-services.md) | [Next: Data Models](data-models.md)