add docs part 2
This commit is contained in:
202
docs/activities.md
Normal file
202
docs/activities.md
Normal 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
607
docs/api-reference.md
Normal 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
261
docs/background-services.md
Normal 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
308
docs/data-models.md
Normal 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
344
docs/error-handling.md
Normal 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
326
docs/local-storage.md
Normal 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
326
docs/settings.md
Normal 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
331
docs/theming.md
Normal 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
444
docs/ui-components.md
Normal 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
291
docs/websocket.md
Normal 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)
|
||||
Reference in New Issue
Block a user