Merge branch 'main' into fix/map-sidepanel-queries

This commit is contained in:
Yaros
2026-04-28 19:58:44 +02:00
838 changed files with 39248 additions and 10341 deletions
+6
View File
@@ -6,6 +6,12 @@ mobile/openapi/**/*.dart linguist-generated=true
mobile/lib/**/*.g.dart -diff -merge mobile/lib/**/*.g.dart -diff -merge
mobile/lib/**/*.g.dart linguist-generated=true mobile/lib/**/*.g.dart linguist-generated=true
mobile/android/**/*.g.kt -diff -merge
mobile/android/**/*.g.kt linguist-generated=true
mobile/ios/**/*.g.swift -diff -merge
mobile/ios/**/*.g.swift linguist-generated=true
mobile/lib/**/*.drift.dart -diff -merge mobile/lib/**/*.drift.dart -diff -merge
mobile/lib/**/*.drift.dart linguist-generated=true mobile/lib/**/*.drift.dart linguist-generated=true
+1 -1
View File
@@ -1 +1 @@
24.14.1 24.15.0
+3 -3
View File
@@ -103,7 +103,7 @@ jobs:
- name: Restore Gradle Cache - name: Restore Gradle Cache
id: cache-gradle-restore id: cache-gradle-restore
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with: with:
path: | path: |
~/.gradle/caches ~/.gradle/caches
@@ -160,7 +160,7 @@ jobs:
- name: Save Gradle Cache - name: Save Gradle Cache
id: cache-gradle-save id: cache-gradle-save
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
with: with:
path: | path: |
@@ -210,7 +210,7 @@ jobs:
working-directory: ./mobile working-directory: ./mobile
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1.300.0 uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
with: with:
ruby-version: '3.3' ruby-version: '3.3'
bundler-cache: true bundler-cache: true
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Check for breaking API changes - name: Check for breaking API changes
uses: oasdiff/oasdiff-action/breaking@e6faebce24cf20ac38653d0d2c7f4aa80aaafc79 # v0.0.38 uses: oasdiff/oasdiff-action/breaking@f8cb9308b42121e793f835bd14c0b8090420430c # v0.0.39
with: with:
base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json base: https://raw.githubusercontent.com/${{ github.repository }}/main/open-api/immich-openapi-specs.json
revision: open-api/immich-openapi-specs.json revision: open-api/immich-openapi-specs.json
+3 -3
View File
@@ -57,7 +57,7 @@ jobs:
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file. # If you wish to specify custom queries, you can do so here or in a config file.
@@ -70,7 +70,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 uses: github/codeql-action/autobuild@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -83,6 +83,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh # ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1 uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with: with:
category: '/language:${{matrix.language}}' category: '/language:${{matrix.language}}'
+1 -1
View File
@@ -211,7 +211,7 @@ jobs:
run: 'mise run //deployment:tf apply' run: 'mise run //deployment:tf apply'
- name: Comment - name: Comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0 uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
if: ${{ steps.parameters.outputs.event == 'pr' }} if: ${{ steps.parameters.outputs.event == 'pr' }}
with: with:
token: ${{ steps.token.outputs.token }} token: ${{ steps.token.outputs.token }}
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
run: 'mise run //deployment:tf destroy -- -refresh=false' run: 'mise run //deployment:tf destroy -- -refresh=false'
- name: Comment - name: Comment
uses: actions-cool/maintain-one-comment@4b2dbf086015f892dcb5e8c1106f5fccd6c1476b # v3.2.0 uses: actions-cool/maintain-one-comment@909842216bc8e8658364c572ec52100f4c2cc50a # v3.3.0
with: with:
token: ${{ steps.token.outputs.token }} token: ${{ steps.token.outputs.token }}
number: ${{ github.event.number }} number: ${{ github.event.number }}
+1 -1
View File
@@ -142,7 +142,7 @@ jobs:
github-token: ${{ steps.generate-token.outputs.token }} github-token: ${{ steps.generate-token.outputs.token }}
- name: Create draft release - name: Create draft release
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1 uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2.6.2
with: with:
draft: true draft: true
tag_name: ${{ needs.bump_version.outputs.version }} tag_name: ${{ needs.bump_version.outputs.version }}
-3
View File
@@ -1,6 +1,3 @@
[submodule "mobile/.isar"]
path = mobile/.isar
url = https://github.com/isar/isar
[submodule "e2e/test-assets"] [submodule "e2e/test-assets"]
path = e2e/test-assets path = e2e/test-assets
url = https://github.com/immich-app/test-assets url = https://github.com/immich-app/test-assets
-12
View File
@@ -13,10 +13,6 @@
"editor.wordBasedSuggestions": "off" "editor.wordBasedSuggestions": "off"
}, },
"[javascript]": { "[javascript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
@@ -29,18 +25,10 @@
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
"[svelte]": { "[svelte]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "svelte.svelte-vscode", "editor.defaultFormatter": "svelte.svelte-vscode",
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
"[typescript]": { "[typescript]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.removeUnusedImports": "explicit"
},
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true "editor.formatOnSave": true
}, },
+1 -1
View File
@@ -1 +1 @@
24.14.1 24.15.0
+1 -1
View File
@@ -68,6 +68,6 @@
"micromatch": "^4.0.8" "micromatch": "^4.0.8"
}, },
"volta": { "volta": {
"node": "24.14.1" "node": "24.15.0"
} }
} }
+2 -2
View File
@@ -1,6 +1,6 @@
[tools] [tools]
terragrunt = "1.0.0" terragrunt = "1.0.2"
opentofu = "1.11.5" opentofu = "1.11.6"
[tasks."tg:fmt"] [tasks."tg:fmt"]
run = "terragrunt hclfmt" run = "terragrunt hclfmt"
+1 -1
View File
@@ -85,7 +85,7 @@ services:
container_name: immich_prometheus container_name: immich_prometheus
ports: ports:
- 9090:9090 - 9090:9090
image: prom/prometheus@sha256:dda13e28bf95a5e5ca5b8ed56852006094c1c8e8871d9c9dbeed30aa6e55271f image: prom/prometheus@sha256:e4254400b85610324913f0dc4acf92603d9984e7519414c5a12811aa6146acc3
volumes: volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml - ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus - prometheus-data:/prometheus
+1 -1
View File
@@ -1 +1 @@
24.14.1 24.15.0
+7
View File
@@ -50,6 +50,10 @@ Before enabling OAuth in Immich, a new client application needs to be configured
- `https://immich.example.com/auth/login` - `https://immich.example.com/auth/login`
- `https://immich.example.com/user-settings` - `https://immich.example.com/user-settings`
3. Configure Backchannel logout URL
If the authentication server supports it, the **Backchannel logout URL** can be specified, and it is of the form: `http://DOMAIN:PORT/api/oauth/backchannel-logout`.
## Enable OAuth ## Enable OAuth
Once you have a new OAuth client application configured, Immich can be configured using the Administration Settings page, available on the web (Administration -> Settings). Once you have a new OAuth client application configured, Immich can be configured using the Administration Settings page, available on the web (Administration -> Settings).
@@ -63,6 +67,8 @@ Once you have a new OAuth client application configured, Immich can be configure
| `scope` | string | openid email profile | Full list of scopes to send with the request (space delimited) | | `scope` | string | openid email profile | Full list of scopes to send with the request (space delimited) |
| `id_token_signed_response_alg` | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) | | `id_token_signed_response_alg` | string | RS256 | The algorithm used to sign the id token (examples: RS256, HS256) |
| `userinfo_signed_response_alg` | string | none | The algorithm used to sign the userinfo response (examples: RS256, HS256) | | `userinfo_signed_response_alg` | string | none | The algorithm used to sign the userinfo response (examples: RS256, HS256) |
| `prompt` | string | (empty) | Prompt parameter for authorization url (examples: select_account, login, consent) |
| `end_session_endpoint` | URL | (empty) | Http(s) alternative end session endpoint (logout URI) |
| Request timeout | string | 30,000 (30 seconds) | Number of milliseconds to wait for http requests to complete before giving up | | Request timeout | string | 30,000 (30 seconds) | Number of milliseconds to wait for http requests to complete before giving up |
| Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** | | Storage Label Claim | string | preferred_username | Claim mapping for the user's storage label**¹** |
| Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** | | Role Claim | string | immich_role | Claim mapping for the user's role. (should return "user" or "admin")**¹** |
@@ -181,6 +187,7 @@ Configuration of OAuth in Immich System Settings
| Scope | openid email profile immich_scope | | Scope | openid email profile immich_scope |
| ID Token Signed Response Algorithm | RS256 | | ID Token Signed Response Algorithm | RS256 |
| Userinfo Signed Response Algorithm | RS256 | | Userinfo Signed Response Algorithm | RS256 |
| End Session Endpoint | https://auth.example.com/logout?rd=https://immich.example.com/ |
| Storage Label Claim | uid | | Storage Label Claim | uid |
| Storage Quota Claim | immich_quota | | Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (empty for unlimited quota) | | Default Storage Quota (GiB) | 0 (empty for unlimited quota) |
+1
View File
@@ -18,6 +18,7 @@ For the full list, refer to the [Immich source code](https://github.com/immich-a
| `JPEG 2000` | `.jp2` | :white_check_mark: | | | `JPEG 2000` | `.jp2` | :white_check_mark: | |
| `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | | | `JPEG` | `.jpeg` `.jpg` `.jpe` `.insp` | :white_check_mark: | |
| `JPEG XL` | `.jxl` | :white_check_mark: | | | `JPEG XL` | `.jxl` | :white_check_mark: | |
| `MPO` | `.mpo` | :white_check_mark: | Multi-Picture |
| `PNG` | `.png` | :white_check_mark: | | | `PNG` | `.png` | :white_check_mark: | |
| `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop | | `PSD` | `.psd` | :white_check_mark: | Adobe Photoshop |
| `RAW` | `.raw` | :white_check_mark: | | | `RAW` | `.raw` | :white_check_mark: | |
+1
View File
@@ -193,6 +193,7 @@ The default configuration looks like this:
"defaultStorageQuota": null, "defaultStorageQuota": null,
"enabled": false, "enabled": false,
"issuerUrl": "", "issuerUrl": "",
"endSessionEndpoint": "",
"mobileOverrideEnabled": false, "mobileOverrideEnabled": false,
"mobileRedirectUri": "", "mobileRedirectUri": "",
"profileSigningAlgorithm": "none", "profileSigningAlgorithm": "none",
+1 -1
View File
@@ -37,7 +37,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices | | `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices | | `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices | | `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `IMMICH_HELMET_FILE` | Path to a json file with [helmet](https://www.npmjs.com/package/helmet) options. Set to `false` to disable. Set to `true` to use `server/helmet.json`. | `false` | server | api, microservices | | `IMMICH_HELMET_FILE` | Path to a json file with [helmet](https://www.npmjs.com/package/helmet) options. Set to `false` to disable. Set to `true` to use `server/helmet.json`. | `false` | server | api |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | | | `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
| `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | | | `CPU_CORES` | Number of cores available to the Immich server | auto-detected CPU core count | server | |
| `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api | | `IMMICH_API_METRICS_PORT` | Port for the OTEL metrics | `8081` | server | api |
+7 -7
View File
@@ -17,10 +17,10 @@
"write-heading-ids": "docusaurus write-heading-ids" "write-heading-ids": "docusaurus write-heading-ids"
}, },
"dependencies": { "dependencies": {
"@docusaurus/core": "~3.9.0", "@docusaurus/core": "~3.10.0",
"@docusaurus/preset-classic": "~3.9.0", "@docusaurus/preset-classic": "~3.10.0",
"@docusaurus/theme-common": "~3.9.0", "@docusaurus/theme-common": "~3.10.0",
"@docusaurus/theme-mermaid": "~3.9.0", "@docusaurus/theme-mermaid": "~3.10.0",
"@mdi/js": "^7.3.67", "@mdi/js": "^7.3.67",
"@mdi/react": "^1.6.1", "@mdi/react": "^1.6.1",
"@mdx-js/react": "^3.0.0", "@mdx-js/react": "^3.0.0",
@@ -36,9 +36,9 @@
"url": "^0.11.0" "url": "^0.11.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "~3.9.0", "@docusaurus/module-type-aliases": "~3.10.0",
"@docusaurus/tsconfig": "^3.10.0", "@docusaurus/tsconfig": "^3.10.0",
"@docusaurus/types": "^3.7.0", "@docusaurus/types": "^3.10.0",
"prettier": "^3.7.4", "prettier": "^3.7.4",
"typescript": "^6.0.0" "typescript": "^6.0.0"
}, },
@@ -58,6 +58,6 @@
"node": ">=20" "node": ">=20"
}, },
"volta": { "volta": {
"node": "24.14.1" "node": "24.15.0"
} }
} }
+31 -5
View File
@@ -1,5 +1,12 @@
import { exportJWK, generateKeyPair } from 'jose'; import {
calculateJwkThumbprint,
exportJWK,
importPKCS8,
importSPKI,
SignJWT,
} from 'jose';
import Provider from 'oidc-provider'; import Provider from 'oidc-provider';
import { PRIVATE_KEY_PEM, PUBLIC_KEY_PEM } from './test-keys';
export enum OAuthClient { export enum OAuthClient {
DEFAULT = 'client-default', DEFAULT = 'client-default',
@@ -44,6 +51,29 @@ const claims = [
}, },
]; ];
const privateKey = await importPKCS8(PRIVATE_KEY_PEM, 'RS256', {
extractable: true,
});
const publicKey = await importSPKI(PUBLIC_KEY_PEM, 'RS256', {
extractable: true,
});
const kid = await calculateJwkThumbprint(await exportJWK(publicKey));
export async function generateLogoutToken(iss: string, sub: string) {
return await new SignJWT({
iss: iss,
aud: OAuthClient.DEFAULT,
iat: Math.floor(Date.now() / 1000),
jti: crypto.randomUUID(),
sub: sub,
events: {
'http://schemas.openid.net/event/backchannel-logout': {},
},
})
.setProtectedHeader({ alg: 'RS256', typ: 'logout+jwt', kid: kid })
.sign(privateKey);
}
const withDefaultClaims = (sub: string) => ({ const withDefaultClaims = (sub: string) => ({
sub, sub,
email: `${sub}@immich.app`, email: `${sub}@immich.app`,
@@ -66,10 +96,6 @@ const getClaims = (sub: string, use?: string) => {
}; };
const setup = async () => { const setup = async () => {
const { privateKey, publicKey } = await generateKeyPair('RS256', {
extractable: true,
});
const redirectUris = [ const redirectUris = [
'http://127.0.0.1:2285/auth/login', 'http://127.0.0.1:2285/auth/login',
'https://photos.immich.app/oauth/mobile-redirect', 'https://photos.immich.app/oauth/mobile-redirect',
+38
View File
@@ -0,0 +1,38 @@
export const PRIVATE_KEY_PEM = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVj5C7hzN3E2HO
TcJ+DN/e2NSTQFj4rPylz4J8xjm8Es7l0k2kK5EEGvUNVGZbw7s055c+6kwP9eqg
B5XFE7+26Fcq1sou6Tbm310kU4dnMW5l2CgwrhaGyb1pNysao0AMLT60dFYqtUwn
ha9ceCsa+ZU1JrknVf3rONtppBvhWoI7CO9XX1keVQ0unHPzCWUjpXTzC8OGEbmB
2w7ZIUf8OfJkd5RZ4OtIpML71W9n13aDxT50x2/EW/pFLFtQ/oaleOKHpvlRXDRX
W86G4moUJym3gHMXMUj2aOcFG2UJnpLruKz3i5qZwYiTRlBP6O9EIQNCVtYxchuN
V1CCcBU1AgMBAAECggEAJLfXMu8Nx89ynPVyyUMMaFfoEpHC9iR0L5obQVpiPMYK
VRqVVLecdftPS9s7eQ58BNBRzdC0ZVu841aRYs3HLNbsZZhPkYZQpAxU//Dg5okY
fzj7Hv5yidt4HN9+Pd8z/3lRMnj4WapifLaBt8xJ2ujJBMBRxzJBsXDnT0+Kx7+y
bYDeuVfyUTEikaK3QZTbuRF3D3eiuN16GG+hv8UqTF2eYbPxdiLjYpTSHa4mH88C
qfJz2Xt4SEzmyeo3G+MO17wDFOwtEe8ojlJfULHnHJSFdUwTfYIFM1bg5/fJ9MOS
/fO3TSG+wkQqjQa6eoGssAzP87fL2XNLzlDtGY/7uQKBgQDHuJHOtf1EjOvNYiP7
EN+8QGs41ghzt9CQRQxWbHpusR3IW3P83KMXwYmrlG70oOUXBRGSB/ESXUofXc5W
pu5+Y55S44aUnu/a9yOBttYW0dtHZSL0zFT+PlVASwUzFZ2zcH1KXlUkSpfL5OAD
PyDDTnBZ2AWh45fRO9wLo6PPuQKBgQC/tI03RqU3mOjqukKbquYeIpXHfRU5Z0DM
u9ru1THYEl6fmkMXycxo/mvW3awyFuyKy/VodqIgKnFgumEqCHZh6OAMm/LC7TfA
l9tjFSs/MyOqQVD4kbX+z6Oq4c4GccDoXfsQ3gzECoBapegi/F+6/25y+/C8ghXb
J/Jg1GQXXQKBgQDFgWbfzuVZZyrBfu4qGLPJDMN7/114YizknwPma3xf/tN/EcGQ
K/k1QvWMMkvPq1UiAKcxjJ0AFjV482FcG9T6NDWbrtmmG88C8Sex3Ue2ZW2+GuwI
vhDHJIlV/Vp0/Elp7DJa2xLDwuh+gCZvz3vs6KL+ljxrrhCyn8mp0PfsMQKBgFFZ
KnuETOO0zVGdzFoGQTQUdP58A5+iQwsdxB+I9Ge+E80iRso3ZbhADj7VPhbbR3D2
b6LuhImluQrUzBpsEOAnU7vGCVPSGdBuIDiBaSKebsn2gYeZPWNtdQQ0YZq2dqek
Cb/0mfIuipzsvf7qnSza62F7q4IyqVegMegI+Jg5AoGATM3NMy7JZeKzSkm+3ohU
3xZOwgqKV9SH+0OeYWpuBxT7D7FlrKKI4NJ3XN3hg2f/DJAF6dH11CPe7pk94yol
HMbh+PQUQ6GYvAzxIOvagWboQ3lzeyubNMpyFjfOrIE/WOQCUBZ9tIwCHIarIuyi
QRuNOj3+U8T/n1Ww352HBdw=
-----END PRIVATE KEY-----`;
export const PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlY+Qu4czdxNhzk3Cfgzf
3tjUk0BY+Kz8pc+CfMY5vBLO5dJNpCuRBBr1DVRmW8O7NOeXPupMD/XqoAeVxRO/
tuhXKtbKLuk25t9dJFOHZzFuZdgoMK4Whsm9aTcrGqNADC0+tHRWKrVMJ4WvXHgr
GvmVNSa5J1X96zjbaaQb4VqCOwjvV19ZHlUNLpxz8wllI6V08wvDhhG5gdsO2SFH
/DnyZHeUWeDrSKTC+9VvZ9d2g8U+dMdvxFv6RSxbUP6GpXjih6b5UVw0V1vOhuJq
FCcpt4BzFzFI9mjnBRtlCZ6S67is94uamcGIk0ZQT+jvRCEDQlbWMXIbjVdQgnAV
NQIDAQAB
-----END PUBLIC KEY-----`;
+1 -1
View File
@@ -1 +1 @@
24.14.1 24.15.0
+1 -1
View File
@@ -58,6 +58,6 @@
"vitest": "^4.0.0" "vitest": "^4.0.0"
}, },
"volta": { "volta": {
"node": "24.14.1" "node": "24.15.0"
} }
} }
-13
View File
@@ -5,79 +5,66 @@ export const errorDto = {
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message: 'Authentication required', message: 'Authentication required',
correlationId: expect.any(String),
}, },
unauthorizedWithMessage: (message: string) => ({ unauthorizedWithMessage: (message: string) => ({
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message, message,
correlationId: expect.any(String),
}), }),
forbidden: { forbidden: {
error: 'Forbidden', error: 'Forbidden',
statusCode: 403, statusCode: 403,
message: expect.any(String), message: expect.any(String),
correlationId: expect.any(String),
}, },
missingPermission: (permission: string) => ({ missingPermission: (permission: string) => ({
error: 'Forbidden', error: 'Forbidden',
statusCode: 403, statusCode: 403,
message: `Missing required permission: ${permission}`, message: `Missing required permission: ${permission}`,
correlationId: expect.any(String),
}), }),
wrongPassword: { wrongPassword: {
error: 'Bad Request', error: 'Bad Request',
statusCode: 400, statusCode: 400,
message: 'Wrong password', message: 'Wrong password',
correlationId: expect.any(String),
}, },
invalidToken: { invalidToken: {
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message: 'Invalid user token', message: 'Invalid user token',
correlationId: expect.any(String),
}, },
invalidShareKey: { invalidShareKey: {
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message: 'Invalid share key', message: 'Invalid share key',
correlationId: expect.any(String),
}, },
passwordRequired: { passwordRequired: {
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message: 'Password required', message: 'Password required',
correlationId: expect.any(String),
}, },
badRequest: (message: any = null) => ({ badRequest: (message: any = null) => ({
error: 'Bad Request', error: 'Bad Request',
statusCode: 400, statusCode: 400,
message: message ?? expect.anything(), message: message ?? expect.anything(),
correlationId: expect.any(String),
}), }),
noPermission: { noPermission: {
error: 'Bad Request', error: 'Bad Request',
statusCode: 400, statusCode: 400,
message: expect.stringContaining('Not found or no'), message: expect.stringContaining('Not found or no'),
correlationId: expect.any(String),
}, },
incorrectLogin: { incorrectLogin: {
error: 'Unauthorized', error: 'Unauthorized',
statusCode: 401, statusCode: 401,
message: 'Incorrect email or password', message: 'Incorrect email or password',
correlationId: expect.any(String),
}, },
alreadyHasAdmin: { alreadyHasAdmin: {
error: 'Bad Request', error: 'Bad Request',
statusCode: 400, statusCode: 400,
message: 'The server already has an admin', message: 'The server already has an admin',
correlationId: expect.any(String),
}, },
invalidEmail: { invalidEmail: {
error: 'Bad Request', error: 'Bad Request',
statusCode: 400, statusCode: 400,
message: ['email must be an email'], message: ['email must be an email'],
correlationId: expect.any(String),
}, },
}; };
+66 -34
View File
@@ -154,23 +154,31 @@ describe('/albums', () => {
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedLink, albumName: user1SharedLink,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedEditorUser, albumName: user1SharedEditorUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedViewerUser, albumName: user1SharedViewerUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user2.userId,
albumName: user2SharedUser, albumName: user2SharedUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
]),
shared: true, shared: true,
}), }),
]), ]),
@@ -184,23 +192,31 @@ describe('/albums', () => {
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedEditorUser, albumName: user1SharedEditorUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedViewerUser, albumName: user1SharedViewerUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedLink, albumName: user1SharedLink,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1NotShared, albumName: user1NotShared,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: false, shared: false,
}), }),
]), ]),
@@ -216,23 +232,31 @@ describe('/albums', () => {
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedEditorUser, albumName: user1SharedEditorUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedViewerUser, albumName: user1SharedViewerUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1SharedLink, albumName: user1SharedLink,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: true, shared: true,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user2.userId,
albumName: user2SharedUser, albumName: user2SharedUser,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user2.userId }) },
]),
shared: true, shared: true,
}), }),
]), ]),
@@ -248,8 +272,10 @@ describe('/albums', () => {
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
ownerId: user1.userId,
albumName: user1NotShared, albumName: user1NotShared,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) },
]),
shared: false, shared: false,
}), }),
]), ]),
@@ -286,13 +312,17 @@ describe('/albums', () => {
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
ownerId: user4.userId,
albumName: user4DeletedAsset, albumName: user4DeletedAsset,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
]),
shared: false, shared: false,
}), }),
expect.objectContaining({ expect.objectContaining({
ownerId: user4.userId,
albumName: user4Empty, albumName: user4Empty,
albumUsers: expect.arrayContaining([
{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user4.userId }) },
]),
shared: false, shared: false,
}), }),
]), ]),
@@ -362,16 +392,17 @@ describe('/albums', () => {
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual({ expect(body).toEqual(
...user2Albums[0], expect.objectContaining({
contributorCounts: [{ userId: user1.userId, assetCount: 1 }], contributorCounts: [{ userId: user1.userId, assetCount: 1 }],
assetCount: 1, assetCount: 1,
lastModifiedAssetTimestamp: expect.any(String), lastModifiedAssetTimestamp: expect.any(String),
endDate: expect.any(String), endDate: expect.any(String),
startDate: expect.any(String), startDate: expect.any(String),
albumUsers: expect.any(Array), albumUsers: expect.any(Array),
shared: true, shared: true,
}); }),
);
}); });
}); });
@@ -397,15 +428,13 @@ describe('/albums', () => {
id: expect.any(String), id: expect.any(String),
createdAt: expect.any(String), createdAt: expect.any(String),
updatedAt: expect.any(String), updatedAt: expect.any(String),
ownerId: user1.userId,
albumName: 'New album', albumName: 'New album',
description: '', description: '',
albumThumbnailAssetId: null, albumThumbnailAssetId: null,
shared: false, shared: false,
albumUsers: [], albumUsers: [{ role: AlbumUserRole.Owner, user: expect.objectContaining({ id: user1.userId }) }],
hasSharedLink: false, hasSharedLink: false,
assetCount: 0, assetCount: 0,
owner: expect.objectContaining({ email: user1.userEmail }),
isActivityEnabled: true, isActivityEnabled: true,
order: AssetOrder.Desc, order: AssetOrder.Desc,
}); });
@@ -621,11 +650,11 @@ describe('/albums', () => {
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual( expect(body).toEqual(
expect.objectContaining({ expect.objectContaining({
albumUsers: [ albumUsers: expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
user: expect.objectContaining({ id: user2.userId }), user: expect.objectContaining({ id: user2.userId }),
}), }),
], ]),
}), }),
); );
}); });
@@ -637,7 +666,7 @@ describe('/albums', () => {
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] }); .send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
expect(status).toBe(400); expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest('Cannot be shared with owner')); expect(body).toEqual(errorDto.badRequest('User already added'));
}); });
it('should not be able to add existing user to shared album', async () => { it('should not be able to add existing user to shared album', async () => {
@@ -663,7 +692,7 @@ describe('/albums', () => {
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }], albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
}); });
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer); expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
const { status } = await request(app) const { status } = await request(app)
.put(`/albums/${album.id}/user/${user2.userId}`) .put(`/albums/${album.id}/user/${user2.userId}`)
@@ -678,7 +707,10 @@ describe('/albums', () => {
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(body).toEqual( expect(body).toEqual(
expect.objectContaining({ expect.objectContaining({
albumUsers: [expect.objectContaining({ role: AlbumUserRole.Editor })], albumUsers: [
expect.objectContaining({ role: AlbumUserRole.Owner }),
expect.objectContaining({ role: AlbumUserRole.Editor }),
],
}), }),
); );
}); });
@@ -689,7 +721,7 @@ describe('/albums', () => {
albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }], albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Viewer }],
}); });
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer); expect(album.albumUsers[1].role).toEqual(AlbumUserRole.Viewer);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/albums/${album.id}/user/${user2.userId}`) .put(`/albums/${album.id}/user/${user2.userId}`)
+94 -11
View File
@@ -1,9 +1,10 @@
import { OAuthClient, OAuthUser } from '@immich/e2e-auth-server'; import { OAuthClient, OAuthUser, generateLogoutToken } from '@immich/e2e-auth-server';
import { import {
LoginResponseDto, LoginResponseDto,
SystemConfigOAuthDto, SystemConfigOAuthDto,
getConfigDefaults, getConfigDefaults,
getMyUser, getMyUser,
getSessions,
startOAuth, startOAuth,
updateConfig, updateConfig,
} from '@immich/sdk'; } from '@immich/sdk';
@@ -88,17 +89,19 @@ describe(`/oauth`, () => {
beforeAll(async () => { beforeAll(async () => {
await utils.resetDatabase(); await utils.resetDatabase();
admin = await utils.adminSetup(); admin = await utils.adminSetup();
await setupOAuth(admin.accessToken, {
enabled: true,
clientId: OAuthClient.DEFAULT,
clientSecret: OAuthClient.DEFAULT,
buttonText: 'Login with Immich',
storageLabelClaim: 'immich_username',
});
}); });
describe('POST /oauth/authorize', () => { describe('POST /oauth/authorize', () => {
beforeAll(async () => {
await setupOAuth(admin.accessToken, {
enabled: true,
clientId: OAuthClient.DEFAULT,
clientSecret: OAuthClient.DEFAULT,
buttonText: 'Login with Immich',
storageLabelClaim: 'immich_username',
});
});
it(`should throw an error if a redirect uri is not provided`, async () => { it(`should throw an error if a redirect uri is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/authorize').send({}); const { status, body } = await request(app).post('/oauth/authorize').send({});
expect(status).toBe(400); expect(status).toBe(400);
@@ -118,9 +121,46 @@ describe(`/oauth`, () => {
expect(params.get('redirect_uri')).toBe('http://127.0.0.1:2285/auth/login'); expect(params.get('redirect_uri')).toBe('http://127.0.0.1:2285/auth/login');
expect(params.get('state')).toBeDefined(); expect(params.get('state')).toBeDefined();
}); });
it('should not include the prompt parameter when not configured', async () => {
const { status, body } = await request(app)
.post('/oauth/authorize')
.send({ redirectUri: 'http://127.0.0.1:2285/auth/login' });
expect(status).toBe(201);
const params = new URL(body.url).searchParams;
expect(params.get('prompt')).toBeNull();
});
it('should include the prompt parameter when configured', async () => {
await setupOAuth(admin.accessToken, {
enabled: true,
clientId: OAuthClient.DEFAULT,
clientSecret: OAuthClient.DEFAULT,
prompt: 'select_account',
});
const { status, body } = await request(app)
.post('/oauth/authorize')
.send({ redirectUri: 'http://127.0.0.1:2285/auth/login' });
expect(status).toBe(201);
const params = new URL(body.url).searchParams;
expect(params.get('prompt')).toBe('select_account');
});
}); });
describe('POST /oauth/callback', () => { describe('POST /oauth/callback', () => {
beforeAll(async () => {
await setupOAuth(admin.accessToken, {
enabled: true,
clientId: OAuthClient.DEFAULT,
clientSecret: OAuthClient.DEFAULT,
buttonText: 'Login with Immich',
storageLabelClaim: 'immich_username',
});
});
it(`should throw an error if a url is not provided`, async () => { it(`should throw an error if a url is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/callback').send({}); const { status, body } = await request(app).post('/oauth/callback').send({});
expect(status).toBe(400); expect(status).toBe(400);
@@ -159,10 +199,9 @@ describe(`/oauth`, () => {
it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => { it(`should throw an error if the codeVerifier doesn't match the challenge`, async () => {
const callbackParams = await loginWithOAuth('oauth-auto-register'); const callbackParams = await loginWithOAuth('oauth-auto-register');
const { codeVerifier } = await loginWithOAuth('oauth-auto-register'); const { codeVerifier } = await loginWithOAuth('oauth-auto-register');
const { status, body } = await request(app) const { status } = await request(app)
.post('/oauth/callback') .post('/oauth/callback')
.send({ ...callbackParams, codeVerifier }); .send({ ...callbackParams, codeVerifier });
console.log(body);
expect(status).toBeGreaterThanOrEqual(400); expect(status).toBeGreaterThanOrEqual(400);
}); });
@@ -334,6 +373,50 @@ describe(`/oauth`, () => {
}); });
}); });
describe(`POST /oauth/backchannel-logout`, () => {
it(`should throw an error if the logout_token is not provided`, async () => {
const { status, body } = await request(app).post('/oauth/backchannel-logout').send({});
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['[logout_token] Invalid input: expected string, received undefined']));
});
it(`should throw an error if an invalid logout token is provided`, async () => {
const { status, body } = await request(app)
.post('/oauth/backchannel-logout')
.send({ logout_token: 'invalid token' });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest('Error backchannel logout: token validation failed'));
});
it(`should logout user if a valid logout token is provided`, async () => {
await setupOAuth(admin.accessToken, {
enabled: true,
clientId: OAuthClient.DEFAULT,
clientSecret: OAuthClient.DEFAULT,
autoRegister: true,
signingAlgorithm: 'RS256',
buttonText: 'Login with Immich',
});
const callbackParams = await loginWithOAuth('backchannel-logout-user');
const { status: callbackStatus, body: callbackBody } = await request(app)
.post('/oauth/callback')
.send(callbackParams);
expect(callbackStatus).toBe(201);
await expect(getSessions({ headers: asBearerAuth(callbackBody.accessToken) })).resolves.toHaveLength(1);
const logoutToken = await generateLogoutToken('http://0.0.0.0:2286', 'backchannel-logout-user');
const { status, body } = await request(app).post('/oauth/backchannel-logout').send({ logout_token: logoutToken });
expect(status).toBe(200);
expect(body).toMatchObject({});
await expect(getSessions({ headers: asBearerAuth(callbackBody.accessToken) })).rejects.toMatchObject({
status: 401,
});
});
});
describe('mobile redirect override', () => { describe('mobile redirect override', () => {
beforeAll(async () => { beforeAll(async () => {
await setupOAuth(admin.accessToken, { await setupOAuth(admin.accessToken, {
@@ -3,6 +3,7 @@
*/ */
import { import {
AlbumUserRole,
AssetTypeEnum, AssetTypeEnum,
AssetVisibility, AssetVisibility,
UserAvatarColor, UserAvatarColor,
@@ -420,9 +421,7 @@ export function getAlbum(
albumThumbnailAssetId: album.thumbnailAssetId, albumThumbnailAssetId: album.thumbnailAssetId,
createdAt: album.createdAt, createdAt: album.createdAt,
updatedAt: album.updatedAt, updatedAt: album.updatedAt,
ownerId: albumOwner.id, albumUsers: [{ user: albumOwner, role: AlbumUserRole.Owner }],
owner: albumOwner,
albumUsers: [], // Empty array for non-shared album
shared: false, shared: false,
hasSharedLink: false, hasSharedLink: false,
isActivityEnabled: true, isActivityEnabled: true,
+1
View File
@@ -223,6 +223,7 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI
'.jp2', '.jp2',
'.jpe', '.jpe',
'.jxl', '.jxl',
'.mpo',
'.svg', '.svg',
'.tif', '.tif',
'.tiff', '.tiff',
@@ -349,7 +349,7 @@ test.describe('Timeline', () => {
expect(visibleMockAssetsYearMonths).toContain(month); expect(visibleMockAssetsYearMonths).toContain(month);
} }
}); });
test('Deep link to last photo, scroll up', async ({ page }) => { test.skip('Deep link to last photo, scroll up', async ({ page }) => {
const lastAsset = assets.at(-1)!; const lastAsset = assets.at(-1)!;
await pageUtils.deepLinkPhotosPage(page, lastAsset.id); await pageUtils.deepLinkPhotosPage(page, lastAsset.id);
@@ -361,7 +361,7 @@ test.describe('Timeline', () => {
await thumbnailUtils.expectInViewport(page, '14e5901f-fd7f-40c0-b186-4d7e7fc67968'); await thumbnailUtils.expectInViewport(page, '14e5901f-fd7f-40c0-b186-4d7e7fc67968');
}); });
test('Deep link to first bucket, scroll down', async ({ page }) => { test.skip('Deep link to first bucket, scroll down', async ({ page }) => {
const lastAsset = assets.at(0)!; const lastAsset = assets.at(0)!;
await pageUtils.deepLinkPhotosPage(page, lastAsset.id); await pageUtils.deepLinkPhotosPage(page, lastAsset.id);
await timelineUtils.locator(page).hover(); await timelineUtils.locator(page).hover();
@@ -440,7 +440,7 @@ test.describe('Timeline', () => {
await thumbnailUtils.expectInViewport(page, asset.id); await thumbnailUtils.expectInViewport(page, asset.id);
await thumbnailUtils.expectSelectedDisabled(page, asset.id); await thumbnailUtils.expectSelectedDisabled(page, asset.id);
}); });
test('Add photos to album', async ({ page }) => { test.skip('Add photos to album', async ({ page }) => {
const album = timelineRestData.album; const album = timelineRestData.album;
await pageUtils.openAlbumPage(page, album.id); await pageUtils.openAlbumPage(page, album.id);
await page.locator('nav button[aria-label="Add photos"]').click(); await page.locator('nav button[aria-label="Add photos"]').click();
@@ -752,7 +752,7 @@ test.describe('Timeline', () => {
await page.getByText('Photos', { exact: true }).click(); await page.getByText('Photos', { exact: true }).click();
await thumbnailUtils.expectInViewport(page, assetToFavorite.id); await thumbnailUtils.expectInViewport(page, assetToFavorite.id);
}); });
test('open /favorites, archive photo, unarchive photo', async ({ page }) => { test.skip('open /favorites, archive photo, unarchive photo', async ({ page }) => {
await pageUtils.openFavorites(page); await pageUtils.openFavorites(page);
const assetToArchive = getAsset(timelineRestData, 'ad31e29f-2069-4574-b9a9-ad86523c92cb')!; const assetToArchive = getAsset(timelineRestData, 'ad31e29f-2069-4574-b9a9-ad86523c92cb')!;
await thumbnailUtils.withAssetId(page, assetToArchive.id).hover(); await thumbnailUtils.withAssetId(page, assetToArchive.id).hover();
+2
View File
@@ -276,9 +276,11 @@
"oauth_button_text": "Button text", "oauth_button_text": "Button text",
"oauth_client_secret_description": "Required for confidential client, or if PKCE (Proof Key for Code Exchange) is not supported for public client.", "oauth_client_secret_description": "Required for confidential client, or if PKCE (Proof Key for Code Exchange) is not supported for public client.",
"oauth_enable_description": "Login with OAuth", "oauth_enable_description": "Login with OAuth",
"oauth_end_session_url_description": "Redirect the user to this URI when they log out.",
"oauth_mobile_redirect_uri": "Mobile redirect URI", "oauth_mobile_redirect_uri": "Mobile redirect URI",
"oauth_mobile_redirect_uri_override": "Mobile redirect URI override", "oauth_mobile_redirect_uri_override": "Mobile redirect URI override",
"oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like ''{callback}''", "oauth_mobile_redirect_uri_override_description": "Enable when OAuth provider does not allow a mobile URI, like ''{callback}''",
"oauth_prompt_description": "Prompt parameter (e.g. select_account, login, consent)",
"oauth_role_claim": "Role Claim", "oauth_role_claim": "Role Claim",
"oauth_role_claim_description": "Automatically grant admin access based on the presence of this claim. The claim may have either 'user' or 'admin'.", "oauth_role_claim_description": "Automatically grant admin access based on the presence of this claim. The claim may have either 'user' or 'admin'.",
"oauth_settings": "OAuth", "oauth_settings": "OAuth",
+4 -4
View File
@@ -48,14 +48,14 @@ FROM python:3.13-slim-trixie@sha256:d168b8d9eb761f4d3fe305ebd04aeb7e7f2de0297cec
RUN apt-get update && \ RUN apt-get update && \
apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \ apt-get install --no-install-recommends -yqq ocl-icd-libopencl1 wget && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-core-2_2.28.4+20760_amd64.deb && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-core-2_2.32.7+21184_amd64.deb && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.28.4/intel-igc-opencl-2_2.28.4+20760_amd64.deb && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/v2.32.7/intel-igc-opencl-2_2.32.7+21184_amd64.deb && \
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/intel-opencl-icd_26.05.37020.3-0_amd64.deb && \ wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/intel-opencl-icd_26.14.37833.4-0_amd64.deb && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-core_1.0.17537.24_amd64.deb && \
wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb && \ wget -nv https://github.com/intel/intel-graphics-compiler/releases/download/igc-1.0.17537.24/intel-igc-opencl_1.0.17537.24_amd64.deb && \
wget -nv https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb && \ wget -nv https://github.com/intel/compute-runtime/releases/download/24.35.30872.36/intel-opencl-icd-legacy1_24.35.30872.36_amd64.deb && \
# TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file # TODO: Figure out how to get renovate to manage this differently versioned libigdgmm file
wget -nv https://github.com/intel/compute-runtime/releases/download/26.05.37020.3/libigdgmm12_22.9.0_amd64.deb && \ wget -nv https://github.com/intel/compute-runtime/releases/download/26.14.37833.4/libigdgmm12_22.9.0_amd64.deb && \
dpkg -i *.deb && \ dpkg -i *.deb && \
rm *.deb && \ rm *.deb && \
apt-get remove wget -yqq && \ apt-get remove wget -yqq && \
+4 -1
View File
@@ -183,7 +183,10 @@ async def predict(
text: str | None = Form(default=None), text: str | None = Form(default=None),
) -> Any: ) -> Any:
if image is not None: if image is not None:
inputs: Image | str = await run(lambda: decode_pil(image)) decoded = await run(lambda: decode_pil(image))
if decoded.width == 0 or decoded.height == 0:
raise HTTPException(400, "Image has zero width or height")
inputs: Image | str = decoded
elif text is not None: elif text is not None:
inputs = text inputs = text
else: else:
+2 -2
View File
@@ -9,12 +9,12 @@ dependencies = [
"aiocache>=0.12.1,<1.0", "aiocache>=0.12.1,<1.0",
"fastapi>=0.95.2,<1.0", "fastapi>=0.95.2,<1.0",
"gunicorn>=21.1.0", "gunicorn>=21.1.0",
"huggingface-hub>=0.20.1,<1.0", "huggingface-hub>=1.0,<2.0",
"insightface>=0.7.3,<1.0", "insightface>=0.7.3,<1.0",
"numpy<2.4.0", "numpy<2.4.0",
"opencv-python-headless>=4.7.0.72,<5.0", "opencv-python-headless>=4.7.0.72,<5.0",
"orjson>=3.9.5", "orjson>=3.9.5",
"pillow>=12.2,<12.3", "pillow>=12.2,<13",
"pydantic>=2.0.0,<3", "pydantic>=2.0.0,<3",
"pydantic-settings>=2.5.2,<3", "pydantic-settings>=2.5.2,<3",
"python-multipart>=0.0.6,<1.0", "python-multipart>=0.0.6,<1.0",
+13
View File
@@ -1198,6 +1198,19 @@ class TestLoad:
mock_model.model_format = ModelFormat.ONNX mock_model.model_format = ModelFormat.ONNX
@pytest.mark.parametrize("size", [(0, 100), (100, 0), (0, 0)])
def test_predict_rejects_empty_image(size: tuple[int, int], deployed_app: TestClient) -> None:
with mock.patch("immich_ml.main.decode_pil", return_value=Image.new("RGB", size)):
response = deployed_app.post(
"http://localhost:3003/predict",
data={"entries": json.dumps({"clip": {"visual": {"modelName": "ViT-B-32__openai"}}})},
files={"image": b"fake image bytes"},
)
assert response.status_code == 400
assert "zero" in response.json()["detail"].lower()
def test_root_endpoint(deployed_app: TestClient) -> None: def test_root_endpoint(deployed_app: TestClient) -> None:
response = deployed_app.get("http://localhost:3003") response = deployed_app.get("http://localhost:3003")
+5 -5
View File
@@ -14,11 +14,11 @@ config_roots = [
] ]
[tools] [tools]
node = "24.14.1" node = "24.15.0"
flutter = "3.35.7" flutter = "3.41.6"
pnpm = "10.33.0" pnpm = "10.33.1"
terragrunt = "1.0.0" terragrunt = "1.0.2"
opentofu = "1.11.5" opentofu = "1.11.6"
java = "21.0.2" java = "21.0.2"
[tools."github:CQLabs/homebrew-dcm"] [tools."github:CQLabs/homebrew-dcm"]
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"dart.flutterSdkPath": ".fvm/versions/3.41.6", "dart.flutterSdkPath": ".fvm/versions/3.41.7",
"dart.lineLength": 120, "dart.lineLength": 120,
"[dart]": { "[dart]": {
"editor.rulers": [ "editor.rulers": [
@@ -43,8 +43,8 @@ class BackgroundEngineLock(context: Context) : BackgroundWorkerLockApi, ImmichPl
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
super.onAttachedToEngine(binding) super.onAttachedToEngine(binding)
checkAndEnforceBackgroundLock(binding.applicationContext)
engineCount.incrementAndGet() engineCount.incrementAndGet()
checkAndEnforceBackgroundLock(binding.applicationContext)
Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount") Log.i(TAG, "Flutter engine attached. Attached Engines count: $engineCount")
} }
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -37,36 +37,150 @@ private object BackgroundWorkerPigeonUtils {
) )
} }
} }
fun doubleEquals(a: Double, b: Double): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN())
}
fun floatEquals(a: Float, b: Float): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN())
}
fun doubleHash(d: Double): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (d == 0.0) 0.0 else d
val bits = java.lang.Double.doubleToLongBits(normalized)
return (bits xor (bits ushr 32)).toInt()
}
fun floatHash(f: Float): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (f == 0.0f) 0.0f else f
return java.lang.Float.floatToIntBits(normalized)
}
fun deepEquals(a: Any?, b: Any?): Boolean { fun deepEquals(a: Any?, b: Any?): Boolean {
if (a === b) {
return true
}
if (a == null || b == null) {
return false
}
if (a is ByteArray && b is ByteArray) { if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is IntArray && b is IntArray) { if (a is IntArray && b is IntArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is LongArray && b is LongArray) { if (a is LongArray && b is LongArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is DoubleArray && b is DoubleArray) { if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b) if (a.size != b.size) return false
for (i in a.indices) {
if (!doubleEquals(a[i], b[i])) return false
}
return true
}
if (a is FloatArray && b is FloatArray) {
if (a.size != b.size) return false
for (i in a.indices) {
if (!floatEquals(a[i], b[i])) return false
}
return true
} }
if (a is Array<*> && b is Array<*>) { if (a is Array<*> && b is Array<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } for (i in a.indices) {
if (!deepEquals(a[i], b[i])) return false
}
return true
} }
if (a is List<*> && b is List<*>) { if (a is List<*> && b is List<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } val iterA = a.iterator()
val iterB = b.iterator()
while (iterA.hasNext() && iterB.hasNext()) {
if (!deepEquals(iterA.next(), iterB.next())) return false
}
return true
} }
if (a is Map<*, *> && b is Map<*, *>) { if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all { if (a.size != b.size) return false
(b as Map<Any?, Any?>).containsKey(it.key) && for (entry in a) {
deepEquals(it.value, b[it.key]) val key = entry.key
var found = false
for (bEntry in b) {
if (deepEquals(key, bEntry.key)) {
if (deepEquals(entry.value, bEntry.value)) {
found = true
break
} else {
return false
}
}
}
if (!found) return false
} }
return true
}
if (a is Double && b is Double) {
return doubleEquals(a, b)
}
if (a is Float && b is Float) {
return floatEquals(a, b)
} }
return a == b return a == b
} }
fun deepHash(value: Any?): Int {
return when (value) {
null -> 0
is ByteArray -> value.contentHashCode()
is IntArray -> value.contentHashCode()
is LongArray -> value.contentHashCode()
is DoubleArray -> {
var result = 1
for (item in value) {
result = 31 * result + doubleHash(item)
}
result
}
is FloatArray -> {
var result = 1
for (item in value) {
result = 31 * result + floatHash(item)
}
result
}
is Array<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is List<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is Map<*, *> -> {
var result = 0
for (entry in value) {
result += ((deepHash(entry.key) * 31) xor deepHash(entry.value))
}
result
}
is Double -> doubleHash(value)
is Float -> floatHash(value)
else -> value.hashCode()
}
}
} }
/** /**
@@ -79,7 +193,7 @@ class FlutterError (
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null
) : Throwable() ) : RuntimeException()
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class BackgroundWorkerSettings ( data class BackgroundWorkerSettings (
@@ -101,15 +215,22 @@ data class BackgroundWorkerSettings (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is BackgroundWorkerSettings) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return BackgroundWorkerPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as BackgroundWorkerSettings
return BackgroundWorkerPigeonUtils.deepEquals(this.requiresCharging, other.requiresCharging) && BackgroundWorkerPigeonUtils.deepEquals(this.minimumDelaySeconds, other.minimumDelaySeconds)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + BackgroundWorkerPigeonUtils.deepHash(this.requiresCharging)
result = 31 * result + BackgroundWorkerPigeonUtils.deepHash(this.minimumDelaySeconds)
return result
}
} }
private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() { private open class BackgroundWorkerPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
@@ -5,8 +5,10 @@ import android.provider.MediaStore
import android.util.Log import android.util.Log
import androidx.work.BackoffPolicy import androidx.work.BackoffPolicy
import androidx.work.Constraints import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager import androidx.work.WorkManager
import io.flutter.embedding.engine.FlutterEngineCache import io.flutter.embedding.engine.FlutterEngineCache
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -18,6 +20,7 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
override fun enable() { override fun enable() {
enqueueMediaObserver(ctx) enqueueMediaObserver(ctx)
enqueuePeriodicWorker(ctx)
} }
override fun saveNotificationMessage(title: String, body: String) { override fun saveNotificationMessage(title: String, body: String) {
@@ -27,12 +30,14 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
override fun configure(settings: BackgroundWorkerSettings) { override fun configure(settings: BackgroundWorkerSettings) {
BackgroundWorkerPreferences(ctx).updateSettings(settings) BackgroundWorkerPreferences(ctx).updateSettings(settings)
enqueueMediaObserver(ctx) enqueueMediaObserver(ctx)
enqueuePeriodicWorker(ctx)
} }
override fun disable() { override fun disable() {
WorkManager.getInstance(ctx).apply { WorkManager.getInstance(ctx).apply {
cancelUniqueWork(OBSERVER_WORKER_NAME) cancelUniqueWork(OBSERVER_WORKER_NAME)
cancelUniqueWork(BACKGROUND_WORKER_NAME) cancelUniqueWork(BACKGROUND_WORKER_NAME)
cancelUniqueWork(PERIODIC_WORKER_NAME)
} }
Log.i(TAG, "Cancelled background upload tasks") Log.i(TAG, "Cancelled background upload tasks")
} }
@@ -40,6 +45,7 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
companion object { companion object {
private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1" private const val BACKGROUND_WORKER_NAME = "immich/BackgroundWorkerV1"
private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1" private const val OBSERVER_WORKER_NAME = "immich/MediaObserverV1"
private const val PERIODIC_WORKER_NAME = "immich/PeriodicBackgroundWorkerV1"
const val ENGINE_CACHE_KEY = "immich::background_worker::engine" const val ENGINE_CACHE_KEY = "immich::background_worker::engine"
@@ -55,7 +61,7 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
setRequiresCharging(settings.requiresCharging) setRequiresCharging(settings.requiresCharging)
}.build() }.build()
val work = OneTimeWorkRequest.Builder(MediaObserver::class.java) val work = OneTimeWorkRequestBuilder<MediaObserver>()
.setConstraints(constraints) .setConstraints(constraints)
.build() .build()
WorkManager.getInstance(ctx) WorkManager.getInstance(ctx)
@@ -67,10 +73,30 @@ class BackgroundWorkerApiImpl(context: Context) : BackgroundWorkerFgHostApi {
) )
} }
fun enqueuePeriodicWorker(ctx: Context) {
val settings = BackgroundWorkerPreferences(ctx).getSettings()
val constraints = Constraints.Builder().apply {
setRequiresCharging(settings.requiresCharging)
}.build()
val work =
PeriodicWorkRequestBuilder<PeriodicWorker>(
1,
TimeUnit.HOURS,
15,
TimeUnit.MINUTES
).setConstraints(constraints)
.build()
WorkManager.getInstance(ctx)
.enqueueUniquePeriodicWork(PERIODIC_WORKER_NAME, ExistingPeriodicWorkPolicy.UPDATE, work)
Log.i(TAG, "Enqueued periodic background worker with name: $PERIODIC_WORKER_NAME")
}
fun enqueueBackgroundWorker(ctx: Context) { fun enqueueBackgroundWorker(ctx: Context) {
val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build() val constraints = Constraints.Builder().setRequiresBatteryNotLow(true).build()
val work = OneTimeWorkRequestBuilder<BackgroundWorker>()
val work = OneTimeWorkRequest.Builder(BackgroundWorker::class.java)
.setConstraints(constraints) .setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.MINUTES)
.build() .build()
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -0,0 +1,16 @@
package app.alextran.immich.background
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
class PeriodicWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
private val ctx: Context = context.applicationContext
override fun doWork(): Result {
Log.i("PeriodicWorker", "Periodic worker triggered, starting background worker")
BackgroundWorkerApiImpl.enqueueBackgroundWorker(ctx)
return Result.success()
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -46,7 +46,7 @@ class FlutterError (
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null
) : Throwable() ) : RuntimeException()
enum class NetworkCapability(val raw: Int) { enum class NetworkCapability(val raw: Int) {
CELLULAR(0), CELLULAR(0),
@@ -75,7 +75,7 @@ private open class ConnectivityPigeonCodec : StandardMessageCodec() {
when (value) { when (value) {
is NetworkCapability -> { is NetworkCapability -> {
stream.write(129) stream.write(129)
writeValue(stream, value.raw) writeValue(stream, value.raw.toLong())
} }
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -34,36 +34,150 @@ private object NetworkPigeonUtils {
) )
} }
} }
fun doubleEquals(a: Double, b: Double): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN())
}
fun floatEquals(a: Float, b: Float): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN())
}
fun doubleHash(d: Double): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (d == 0.0) 0.0 else d
val bits = java.lang.Double.doubleToLongBits(normalized)
return (bits xor (bits ushr 32)).toInt()
}
fun floatHash(f: Float): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (f == 0.0f) 0.0f else f
return java.lang.Float.floatToIntBits(normalized)
}
fun deepEquals(a: Any?, b: Any?): Boolean { fun deepEquals(a: Any?, b: Any?): Boolean {
if (a === b) {
return true
}
if (a == null || b == null) {
return false
}
if (a is ByteArray && b is ByteArray) { if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is IntArray && b is IntArray) { if (a is IntArray && b is IntArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is LongArray && b is LongArray) { if (a is LongArray && b is LongArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is DoubleArray && b is DoubleArray) { if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b) if (a.size != b.size) return false
for (i in a.indices) {
if (!doubleEquals(a[i], b[i])) return false
}
return true
}
if (a is FloatArray && b is FloatArray) {
if (a.size != b.size) return false
for (i in a.indices) {
if (!floatEquals(a[i], b[i])) return false
}
return true
} }
if (a is Array<*> && b is Array<*>) { if (a is Array<*> && b is Array<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } for (i in a.indices) {
if (!deepEquals(a[i], b[i])) return false
}
return true
} }
if (a is List<*> && b is List<*>) { if (a is List<*> && b is List<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } val iterA = a.iterator()
val iterB = b.iterator()
while (iterA.hasNext() && iterB.hasNext()) {
if (!deepEquals(iterA.next(), iterB.next())) return false
}
return true
} }
if (a is Map<*, *> && b is Map<*, *>) { if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all { if (a.size != b.size) return false
(b as Map<Any?, Any?>).containsKey(it.key) && for (entry in a) {
deepEquals(it.value, b[it.key]) val key = entry.key
var found = false
for (bEntry in b) {
if (deepEquals(key, bEntry.key)) {
if (deepEquals(entry.value, bEntry.value)) {
found = true
break
} else {
return false
}
}
}
if (!found) return false
} }
return true
}
if (a is Double && b is Double) {
return doubleEquals(a, b)
}
if (a is Float && b is Float) {
return floatEquals(a, b)
} }
return a == b return a == b
} }
fun deepHash(value: Any?): Int {
return when (value) {
null -> 0
is ByteArray -> value.contentHashCode()
is IntArray -> value.contentHashCode()
is LongArray -> value.contentHashCode()
is DoubleArray -> {
var result = 1
for (item in value) {
result = 31 * result + doubleHash(item)
}
result
}
is FloatArray -> {
var result = 1
for (item in value) {
result = 31 * result + floatHash(item)
}
result
}
is Array<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is List<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is Map<*, *> -> {
var result = 0
for (entry in value) {
result += ((deepHash(entry.key) * 31) xor deepHash(entry.value))
}
result
}
is Double -> doubleHash(value)
is Float -> floatHash(value)
else -> value.hashCode()
}
}
} }
/** /**
@@ -76,7 +190,7 @@ class FlutterError (
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null
) : Throwable() ) : RuntimeException()
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
data class ClientCertData ( data class ClientCertData (
@@ -98,15 +212,22 @@ data class ClientCertData (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is ClientCertData) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return NetworkPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as ClientCertData
return NetworkPigeonUtils.deepEquals(this.data, other.data) && NetworkPigeonUtils.deepEquals(this.password, other.password)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + NetworkPigeonUtils.deepHash(this.data)
result = 31 * result + NetworkPigeonUtils.deepHash(this.password)
return result
}
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
@@ -135,15 +256,24 @@ data class ClientCertPrompt (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is ClientCertPrompt) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return NetworkPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as ClientCertPrompt
return NetworkPigeonUtils.deepEquals(this.title, other.title) && NetworkPigeonUtils.deepEquals(this.message, other.message) && NetworkPigeonUtils.deepEquals(this.cancel, other.cancel) && NetworkPigeonUtils.deepEquals(this.confirm, other.confirm)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + NetworkPigeonUtils.deepHash(this.title)
result = 31 * result + NetworkPigeonUtils.deepHash(this.message)
result = 31 * result + NetworkPigeonUtils.deepHash(this.cancel)
result = 31 * result + NetworkPigeonUtils.deepHash(this.confirm)
return result
}
} }
private open class NetworkPigeonCodec : StandardMessageCodec() { private open class NetworkPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -46,7 +46,7 @@ class FlutterError (
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null
) : Throwable() ) : RuntimeException()
private open class LocalImagesPigeonCodec : StandardMessageCodec() { private open class LocalImagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return super.readValueOfType(type, buffer) return super.readValueOfType(type, buffer)
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -34,36 +34,150 @@ private object MessagesPigeonUtils {
) )
} }
} }
fun doubleEquals(a: Double, b: Double): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN())
}
fun floatEquals(a: Float, b: Float): Boolean {
// Normalize -0.0 to 0.0 and handle NaN equality.
return (if (a == 0.0f) 0.0f else a) == (if (b == 0.0f) 0.0f else b) || (a.isNaN() && b.isNaN())
}
fun doubleHash(d: Double): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (d == 0.0) 0.0 else d
val bits = java.lang.Double.doubleToLongBits(normalized)
return (bits xor (bits ushr 32)).toInt()
}
fun floatHash(f: Float): Int {
// Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes.
val normalized = if (f == 0.0f) 0.0f else f
return java.lang.Float.floatToIntBits(normalized)
}
fun deepEquals(a: Any?, b: Any?): Boolean { fun deepEquals(a: Any?, b: Any?): Boolean {
if (a === b) {
return true
}
if (a == null || b == null) {
return false
}
if (a is ByteArray && b is ByteArray) { if (a is ByteArray && b is ByteArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is IntArray && b is IntArray) { if (a is IntArray && b is IntArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is LongArray && b is LongArray) { if (a is LongArray && b is LongArray) {
return a.contentEquals(b) return a.contentEquals(b)
} }
if (a is DoubleArray && b is DoubleArray) { if (a is DoubleArray && b is DoubleArray) {
return a.contentEquals(b) if (a.size != b.size) return false
for (i in a.indices) {
if (!doubleEquals(a[i], b[i])) return false
}
return true
}
if (a is FloatArray && b is FloatArray) {
if (a.size != b.size) return false
for (i in a.indices) {
if (!floatEquals(a[i], b[i])) return false
}
return true
} }
if (a is Array<*> && b is Array<*>) { if (a is Array<*> && b is Array<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } for (i in a.indices) {
if (!deepEquals(a[i], b[i])) return false
}
return true
} }
if (a is List<*> && b is List<*>) { if (a is List<*> && b is List<*>) {
return a.size == b.size && if (a.size != b.size) return false
a.indices.all{ deepEquals(a[it], b[it]) } val iterA = a.iterator()
val iterB = b.iterator()
while (iterA.hasNext() && iterB.hasNext()) {
if (!deepEquals(iterA.next(), iterB.next())) return false
}
return true
} }
if (a is Map<*, *> && b is Map<*, *>) { if (a is Map<*, *> && b is Map<*, *>) {
return a.size == b.size && a.all { if (a.size != b.size) return false
(b as Map<Any?, Any?>).containsKey(it.key) && for (entry in a) {
deepEquals(it.value, b[it.key]) val key = entry.key
var found = false
for (bEntry in b) {
if (deepEquals(key, bEntry.key)) {
if (deepEquals(entry.value, bEntry.value)) {
found = true
break
} else {
return false
}
}
}
if (!found) return false
} }
return true
}
if (a is Double && b is Double) {
return doubleEquals(a, b)
}
if (a is Float && b is Float) {
return floatEquals(a, b)
} }
return a == b return a == b
} }
fun deepHash(value: Any?): Int {
return when (value) {
null -> 0
is ByteArray -> value.contentHashCode()
is IntArray -> value.contentHashCode()
is LongArray -> value.contentHashCode()
is DoubleArray -> {
var result = 1
for (item in value) {
result = 31 * result + doubleHash(item)
}
result
}
is FloatArray -> {
var result = 1
for (item in value) {
result = 31 * result + floatHash(item)
}
result
}
is Array<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is List<*> -> {
var result = 1
for (item in value) {
result = 31 * result + deepHash(item)
}
result
}
is Map<*, *> -> {
var result = 0
for (entry in value) {
result += ((deepHash(entry.key) * 31) xor deepHash(entry.value))
}
result
}
is Double -> doubleHash(value)
is Float -> floatHash(value)
else -> value.hashCode()
}
}
} }
/** /**
@@ -76,7 +190,7 @@ class FlutterError (
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null
) : Throwable() ) : RuntimeException()
enum class PlatformAssetPlaybackStyle(val raw: Int) { enum class PlatformAssetPlaybackStyle(val raw: Int) {
UNKNOWN(0), UNKNOWN(0),
@@ -102,7 +216,7 @@ data class PlatformAsset (
val updatedAt: Long? = null, val updatedAt: Long? = null,
val width: Long? = null, val width: Long? = null,
val height: Long? = null, val height: Long? = null,
val durationInSeconds: Long, val durationMs: Long,
val orientation: Long, val orientation: Long,
val isFavorite: Boolean, val isFavorite: Boolean,
val adjustmentTime: Long? = null, val adjustmentTime: Long? = null,
@@ -120,14 +234,14 @@ data class PlatformAsset (
val updatedAt = pigeonVar_list[4] as Long? val updatedAt = pigeonVar_list[4] as Long?
val width = pigeonVar_list[5] as Long? val width = pigeonVar_list[5] as Long?
val height = pigeonVar_list[6] as Long? val height = pigeonVar_list[6] as Long?
val durationInSeconds = pigeonVar_list[7] as Long val durationMs = pigeonVar_list[7] as Long
val orientation = pigeonVar_list[8] as Long val orientation = pigeonVar_list[8] as Long
val isFavorite = pigeonVar_list[9] as Boolean val isFavorite = pigeonVar_list[9] as Boolean
val adjustmentTime = pigeonVar_list[10] as Long? val adjustmentTime = pigeonVar_list[10] as Long?
val latitude = pigeonVar_list[11] as Double? val latitude = pigeonVar_list[11] as Double?
val longitude = pigeonVar_list[12] as Double? val longitude = pigeonVar_list[12] as Double?
val playbackStyle = pigeonVar_list[13] as PlatformAssetPlaybackStyle val playbackStyle = pigeonVar_list[13] as PlatformAssetPlaybackStyle
return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationInSeconds, orientation, isFavorite, adjustmentTime, latitude, longitude, playbackStyle) return PlatformAsset(id, name, type, createdAt, updatedAt, width, height, durationMs, orientation, isFavorite, adjustmentTime, latitude, longitude, playbackStyle)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
@@ -139,7 +253,7 @@ data class PlatformAsset (
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
orientation, orientation,
isFavorite, isFavorite,
adjustmentTime, adjustmentTime,
@@ -149,15 +263,34 @@ data class PlatformAsset (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is PlatformAsset) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as PlatformAsset
return MessagesPigeonUtils.deepEquals(this.id, other.id) && MessagesPigeonUtils.deepEquals(this.name, other.name) && MessagesPigeonUtils.deepEquals(this.type, other.type) && MessagesPigeonUtils.deepEquals(this.createdAt, other.createdAt) && MessagesPigeonUtils.deepEquals(this.updatedAt, other.updatedAt) && MessagesPigeonUtils.deepEquals(this.width, other.width) && MessagesPigeonUtils.deepEquals(this.height, other.height) && MessagesPigeonUtils.deepEquals(this.durationMs, other.durationMs) && MessagesPigeonUtils.deepEquals(this.orientation, other.orientation) && MessagesPigeonUtils.deepEquals(this.isFavorite, other.isFavorite) && MessagesPigeonUtils.deepEquals(this.adjustmentTime, other.adjustmentTime) && MessagesPigeonUtils.deepEquals(this.latitude, other.latitude) && MessagesPigeonUtils.deepEquals(this.longitude, other.longitude) && MessagesPigeonUtils.deepEquals(this.playbackStyle, other.playbackStyle)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + MessagesPigeonUtils.deepHash(this.id)
result = 31 * result + MessagesPigeonUtils.deepHash(this.name)
result = 31 * result + MessagesPigeonUtils.deepHash(this.type)
result = 31 * result + MessagesPigeonUtils.deepHash(this.createdAt)
result = 31 * result + MessagesPigeonUtils.deepHash(this.updatedAt)
result = 31 * result + MessagesPigeonUtils.deepHash(this.width)
result = 31 * result + MessagesPigeonUtils.deepHash(this.height)
result = 31 * result + MessagesPigeonUtils.deepHash(this.durationMs)
result = 31 * result + MessagesPigeonUtils.deepHash(this.orientation)
result = 31 * result + MessagesPigeonUtils.deepHash(this.isFavorite)
result = 31 * result + MessagesPigeonUtils.deepHash(this.adjustmentTime)
result = 31 * result + MessagesPigeonUtils.deepHash(this.latitude)
result = 31 * result + MessagesPigeonUtils.deepHash(this.longitude)
result = 31 * result + MessagesPigeonUtils.deepHash(this.playbackStyle)
return result
}
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
@@ -189,15 +322,25 @@ data class PlatformAlbum (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is PlatformAlbum) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as PlatformAlbum
return MessagesPigeonUtils.deepEquals(this.id, other.id) && MessagesPigeonUtils.deepEquals(this.name, other.name) && MessagesPigeonUtils.deepEquals(this.updatedAt, other.updatedAt) && MessagesPigeonUtils.deepEquals(this.isCloud, other.isCloud) && MessagesPigeonUtils.deepEquals(this.assetCount, other.assetCount)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + MessagesPigeonUtils.deepHash(this.id)
result = 31 * result + MessagesPigeonUtils.deepHash(this.name)
result = 31 * result + MessagesPigeonUtils.deepHash(this.updatedAt)
result = 31 * result + MessagesPigeonUtils.deepHash(this.isCloud)
result = 31 * result + MessagesPigeonUtils.deepHash(this.assetCount)
return result
}
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
@@ -226,15 +369,24 @@ data class SyncDelta (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is SyncDelta) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as SyncDelta
return MessagesPigeonUtils.deepEquals(this.hasChanges, other.hasChanges) && MessagesPigeonUtils.deepEquals(this.updates, other.updates) && MessagesPigeonUtils.deepEquals(this.deletes, other.deletes) && MessagesPigeonUtils.deepEquals(this.assetAlbums, other.assetAlbums)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + MessagesPigeonUtils.deepHash(this.hasChanges)
result = 31 * result + MessagesPigeonUtils.deepHash(this.updates)
result = 31 * result + MessagesPigeonUtils.deepHash(this.deletes)
result = 31 * result + MessagesPigeonUtils.deepHash(this.assetAlbums)
return result
}
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
@@ -260,15 +412,23 @@ data class HashResult (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is HashResult) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as HashResult
return MessagesPigeonUtils.deepEquals(this.assetId, other.assetId) && MessagesPigeonUtils.deepEquals(this.error, other.error) && MessagesPigeonUtils.deepEquals(this.hash, other.hash)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + MessagesPigeonUtils.deepHash(this.assetId)
result = 31 * result + MessagesPigeonUtils.deepHash(this.error)
result = 31 * result + MessagesPigeonUtils.deepHash(this.hash)
return result
}
} }
/** Generated class from Pigeon that represents data sent in messages. */ /** Generated class from Pigeon that represents data sent in messages. */
@@ -294,15 +454,23 @@ data class CloudIdResult (
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other !is CloudIdResult) { if (other == null || other.javaClass != javaClass) {
return false return false
} }
if (this === other) { if (this === other) {
return true return true
} }
return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } val other = other as CloudIdResult
return MessagesPigeonUtils.deepEquals(this.assetId, other.assetId) && MessagesPigeonUtils.deepEquals(this.error, other.error) && MessagesPigeonUtils.deepEquals(this.cloudId, other.cloudId)
}
override fun hashCode(): Int = toList().hashCode() override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + MessagesPigeonUtils.deepHash(this.assetId)
result = 31 * result + MessagesPigeonUtils.deepHash(this.error)
result = 31 * result + MessagesPigeonUtils.deepHash(this.cloudId)
return result
}
} }
private open class MessagesPigeonCodec : StandardMessageCodec() { private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
@@ -344,7 +512,7 @@ private open class MessagesPigeonCodec : StandardMessageCodec() {
when (value) { when (value) {
is PlatformAssetPlaybackStyle -> { is PlatformAssetPlaybackStyle -> {
stream.write(129) stream.write(129)
writeValue(stream, value.raw) writeValue(stream, value.raw.toLong())
} }
is PlatformAsset -> { is PlatformAsset -> {
stream.write(130) stream.write(130)
@@ -178,7 +178,7 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin() {
val height = c.getInt(heightColumn).toLong() val height = c.getInt(heightColumn).toLong()
// Duration is milliseconds // Duration is milliseconds
val duration = if (rawMediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0L val duration = if (rawMediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) 0L
else c.getLong(durationColumn) / 1000 else c.getLong(durationColumn)
val orientation = c.getInt(orientationColumn) val orientation = c.getInt(orientationColumn)
val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0 val isFavorite = if (favoriteColumn == -1) false else c.getInt(favoriteColumn) != 0
+1 -1
View File
@@ -46,6 +46,6 @@ material = { module = "com.google.android.material:material", version.ref = "mat
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
# TODO: update to version.ref = "kotlin" when background_downloader is removed # TODO: update to version.ref = "kotlin" when background_downloader is removed
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "1.9.22" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "2.1.0" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
+1 -1
View File
@@ -21,7 +21,7 @@ plugins {
id "com.android.application" version "8.11.2" apply false id "com.android.application" version "8.11.2" apply false
id "org.jetbrains.kotlin.android" version "2.2.20" apply false id "org.jetbrains.kotlin.android" version "2.2.20" apply false
// TODO: update to match kotlin version when background_downloader is removed // TODO: update to match kotlin version when background_downloader is removed
id "org.jetbrains.kotlin.plugin.serialization" version "1.9.22" apply false id "org.jetbrains.kotlin.plugin.serialization" version "2.1.0" apply false
id "com.google.devtools.ksp" version "2.2.20-2.0.3" apply false id "com.google.devtools.ksp" version "2.2.20-2.0.3" apply false
} }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+18 -62
View File
@@ -20,19 +20,21 @@ PODS:
- Flutter - Flutter
- flutter_udid (0.0.1): - flutter_udid (0.0.1):
- Flutter - Flutter
- SAMKeychain - KeychainAccess
- flutter_web_auth_2 (3.0.0): - flutter_web_auth_2 (5.0.0):
- Flutter - Flutter
- fluttertoast (0.0.2): - fluttertoast (0.0.2):
- Flutter - Flutter
- geolocator_apple (1.2.0): - geolocator_apple (1.2.0):
- Flutter - Flutter
- FlutterMacOS
- home_widget (0.0.1): - home_widget (0.0.1):
- Flutter - Flutter
- image_picker_ios (0.0.1): - image_picker_ios (0.0.1):
- Flutter - Flutter
- integration_test (0.0.1): - integration_test (0.0.1):
- Flutter - Flutter
- KeychainAccess (4.2.2)
- local_auth_darwin (0.0.1): - local_auth_darwin (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -44,19 +46,13 @@ PODS:
- Flutter - Flutter
- network_info_plus (0.0.1): - network_info_plus (0.0.1):
- Flutter - Flutter
- objective_c (0.0.1):
- Flutter
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0): - permission_handler_apple (9.3.0):
- Flutter - Flutter
- photo_manager (3.7.1): - photo_manager (3.9.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- SAMKeychain (1.5.3)
- share_handler_ios (0.0.14): - share_handler_ios (0.0.14):
- Flutter - Flutter
- share_handler_ios/share_handler_ios_models (= 0.0.14) - share_handler_ios/share_handler_ios_models (= 0.0.14)
@@ -70,28 +66,6 @@ PODS:
- shared_preferences_foundation (0.0.1): - shared_preferences_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- sqlite3 (3.49.2):
- sqlite3/common (= 3.49.2)
- sqlite3/common (3.49.2)
- sqlite3/dbstatvtab (3.49.2):
- sqlite3/common
- sqlite3/fts5 (3.49.2):
- sqlite3/common
- sqlite3/perf-threadsafe (3.49.2):
- sqlite3/common
- sqlite3/rtree (3.49.2):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- FlutterMacOS
- sqlite3 (~> 3.49.1)
- sqlite3/dbstatvtab
- sqlite3/fts5
- sqlite3/perf-threadsafe
- sqlite3/rtree
- url_launcher_ios (0.0.1): - url_launcher_ios (0.0.1):
- Flutter - Flutter
- wakelock_plus (0.0.1): - wakelock_plus (0.0.1):
@@ -110,7 +84,7 @@ DEPENDENCIES:
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`) - flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`) - flutter_web_auth_2 (from `.symlinks/plugins/flutter_web_auth_2/ios`)
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`) - geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- home_widget (from `.symlinks/plugins/home_widget/ios`) - home_widget (from `.symlinks/plugins/home_widget/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- integration_test (from `.symlinks/plugins/integration_test/ios`) - integration_test (from `.symlinks/plugins/integration_test/ios`)
@@ -118,25 +92,20 @@ DEPENDENCIES:
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`) - maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
- native_video_player (from `.symlinks/plugins/native_video_player/ios`) - native_video_player (from `.symlinks/plugins/native_video_player/ios`)
- network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`)
- objective_c (from `.symlinks/plugins/objective_c/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- photo_manager (from `.symlinks/plugins/photo_manager/ios`) - photo_manager (from `.symlinks/plugins/photo_manager/darwin`)
- share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`) - share_handler_ios (from `.symlinks/plugins/share_handler_ios/ios`)
- share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`) - share_handler_ios_models (from `.symlinks/plugins/share_handler_ios/ios/Models`)
- share_plus (from `.symlinks/plugins/share_plus/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- KeychainAccess
- MapLibre - MapLibre
- SAMKeychain
- sqlite3
EXTERNAL SOURCES: EXTERNAL SOURCES:
background_downloader: background_downloader:
@@ -164,7 +133,7 @@ EXTERNAL SOURCES:
fluttertoast: fluttertoast:
:path: ".symlinks/plugins/fluttertoast/ios" :path: ".symlinks/plugins/fluttertoast/ios"
geolocator_apple: geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/ios" :path: ".symlinks/plugins/geolocator_apple/darwin"
home_widget: home_widget:
:path: ".symlinks/plugins/home_widget/ios" :path: ".symlinks/plugins/home_widget/ios"
image_picker_ios: image_picker_ios:
@@ -179,16 +148,12 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/native_video_player/ios" :path: ".symlinks/plugins/native_video_player/ios"
network_info_plus: network_info_plus:
:path: ".symlinks/plugins/network_info_plus/ios" :path: ".symlinks/plugins/network_info_plus/ios"
objective_c:
:path: ".symlinks/plugins/objective_c/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple: permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios" :path: ".symlinks/plugins/permission_handler_apple/ios"
photo_manager: photo_manager:
:path: ".symlinks/plugins/photo_manager/ios" :path: ".symlinks/plugins/photo_manager/darwin"
share_handler_ios: share_handler_ios:
:path: ".symlinks/plugins/share_handler_ios/ios" :path: ".symlinks/plugins/share_handler_ios/ios"
share_handler_ios_models: share_handler_ios_models:
@@ -197,10 +162,6 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/share_plus/ios" :path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation: shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin" :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/darwin"
url_launcher_ios: url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios" :path: ".symlinks/plugins/url_launcher_ios/ios"
wakelock_plus: wakelock_plus:
@@ -216,32 +177,27 @@ SPEC CHECKSUMS:
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100 flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13 flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9 flutter_udid: 92a5d31fe0526b7b6002a2318df702e12e7eb300
flutter_web_auth_2: 5c8d9dcd7848b5a9efb086d24e7a9adcae979c80 flutter_web_auth_2: 646fc9df97a01c59e5eea99b237da2c6360f8439
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1 fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
geolocator_apple: 1560c3c875af2a412242c7a923e15d0d401966ff geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326 image_picker_ios: e0ece4aa2a75771a7de3fa735d26d90817041326
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391 KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd MapLibre: 69e572367f4ef6287e18246cfafc39c80cdcabcd
maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f maplibre_gl: 3c924e44725147b03dda33430ad216005b40555f
native_video_player: b65c58951ede2f93d103a25366bdebca95081265 native_video_player: b65c58951ede2f93d103a25366bdebca95081265
network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc network_info_plus: cf61925ab5205dce05a4f0895989afdb6aade5fc
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
photo_manager: 1d80ae07a89a67dfbcae95953a1e5a24af7c3e62 photo_manager: 25fd77df14f4f0ba5ef99e2c61814dde77e2bceb
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb share_handler_ios: e2244e990f826b2c8eaa291ac3831569438ba0fb
share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871 share_handler_ios_models: fc638c9b4330dc7f082586c92aee9dfa0b87b871
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
sqlite3: 3c950dc86011117c307eb0b28c4a7bb449dce9f1
sqlite3_flutter_libs: f8fc13346870e73fe35ebf6dbb997fbcd156b241
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556 wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
PODFILE CHECKSUM: 938abbae4114b9c2140c550a2a0d8f7c674f5dfe PODFILE CHECKSUM: 938abbae4114b9c2140c550a2a0d8f7c674f5dfe
+16 -16
View File
@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 77; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -751,7 +751,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -760,7 +760,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.121.0; MARKETING_VERSION = 1.121.0;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile;
PRODUCT_NAME = "Immich-Profile"; PRODUCT_NAME = "Immich-Profile";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -895,7 +895,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -904,7 +904,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.121.0; MARKETING_VERSION = 1.121.0;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug;
PRODUCT_NAME = "Immich-Debug"; PRODUCT_NAME = "Immich-Debug";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -925,7 +925,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
@@ -958,7 +958,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -975,7 +975,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.Widget; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.Widget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
@@ -1001,7 +1001,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1041,7 +1041,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1057,7 +1057,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.Widget; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.Widget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1081,7 +1081,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1098,7 +1098,7 @@
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
@@ -1125,7 +1125,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1166,7 +1166,7 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240; CURRENT_PROJECT_VERSION = 240;
CUSTOM_GROUP_ID = group.app.immich.share; CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79; DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_USER_SCRIPT_SANDBOXING = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17; GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1182,7 +1182,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES; LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension; PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile.ShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
-8
View File
@@ -1,12 +1,4 @@
import BackgroundTasks
import Flutter
import native_video_player import native_video_player
import network_info_plus
import path_provider_foundation
import permission_handler_apple
import photo_manager
import shared_preferences_foundation
import UIKit
@main @main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
+87 -34
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -32,7 +32,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
@@ -50,6 +50,19 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
return value as! T? return value as! T?
} }
private func doubleEqualsBackgroundWorker(_ lhs: Double, _ rhs: Double) -> Bool {
return (lhs.isNaN && rhs.isNaN) || lhs == rhs
}
private func doubleHashBackgroundWorker(_ value: Double, _ hasher: inout Hasher) {
if value.isNaN {
hasher.combine(0x7FF8000000000000)
} else {
// Normalize -0.0 to 0.0
hasher.combine(value == 0 ? 0 : value)
}
}
func deepEqualsBackgroundWorker(_ lhs: Any?, _ rhs: Any?) -> Bool { func deepEqualsBackgroundWorker(_ lhs: Any?, _ rhs: Any?) -> Bool {
let cleanLhs = nilOrValue(lhs) as Any? let cleanLhs = nilOrValue(lhs) as Any?
let cleanRhs = nilOrValue(rhs) as Any? let cleanRhs = nilOrValue(rhs) as Any?
@@ -60,59 +73,92 @@ func deepEqualsBackgroundWorker(_ lhs: Any?, _ rhs: Any?) -> Bool {
case (nil, _), (_, nil): case (nil, _), (_, nil):
return false return false
case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs:
return true
case is (Void, Void): case is (Void, Void):
return true return true
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): case (let lhsArray, let rhsArray) as ([Any?], [Any?]):
return cleanLhsHashable == cleanRhsHashable guard lhsArray.count == rhsArray.count else { return false }
for (index, element) in lhsArray.enumerated() {
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): if !deepEqualsBackgroundWorker(element, rhsArray[index]) {
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
for (index, element) in cleanLhsArray.enumerated() {
if !deepEqualsBackgroundWorker(element, cleanRhsArray[index]) {
return false return false
} }
} }
return true return true
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): case (let lhsArray, let rhsArray) as ([Double], [Double]):
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } guard lhsArray.count == rhsArray.count else { return false }
for (key, cleanLhsValue) in cleanLhsDictionary { for (index, element) in lhsArray.enumerated() {
guard cleanRhsDictionary.index(forKey: key) != nil else { return false } if !doubleEqualsBackgroundWorker(element, rhsArray[index]) {
if !deepEqualsBackgroundWorker(cleanLhsValue, cleanRhsDictionary[key]!) {
return false return false
} }
} }
return true return true
case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard lhsDictionary.count == rhsDictionary.count else { return false }
for (lhsKey, lhsValue) in lhsDictionary {
var found = false
for (rhsKey, rhsValue) in rhsDictionary {
if deepEqualsBackgroundWorker(lhsKey, rhsKey) {
if deepEqualsBackgroundWorker(lhsValue, rhsValue) {
found = true
break
} else {
return false
}
}
}
if !found { return false }
}
return true
case (let lhs as Double, let rhs as Double):
return doubleEqualsBackgroundWorker(lhs, rhs)
case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable):
return lhsHashable == rhsHashable
default: default:
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
return false return false
} }
} }
func deepHashBackgroundWorker(value: Any?, hasher: inout Hasher) { func deepHashBackgroundWorker(value: Any?, hasher: inout Hasher) {
if let valueList = value as? [AnyHashable] { let cleanValue = nilOrValue(value) as Any?
for item in valueList { deepHashBackgroundWorker(value: item, hasher: &hasher) } if let cleanValue = cleanValue {
return if let doubleValue = cleanValue as? Double {
} doubleHashBackgroundWorker(doubleValue, &hasher)
} else if let valueList = cleanValue as? [Any?] {
if let valueDict = value as? [AnyHashable: AnyHashable] { for item in valueList {
for key in valueDict.keys { deepHashBackgroundWorker(value: item, hasher: &hasher)
hasher.combine(key) }
deepHashBackgroundWorker(value: valueDict[key]!, hasher: &hasher) } else if let valueList = cleanValue as? [Double] {
for item in valueList {
doubleHashBackgroundWorker(item, &hasher)
}
} else if let valueDict = cleanValue as? [AnyHashable: Any?] {
var result = 0
for (key, value) in valueDict {
var entryKeyHasher = Hasher()
deepHashBackgroundWorker(value: key, hasher: &entryKeyHasher)
var entryValueHasher = Hasher()
deepHashBackgroundWorker(value: value, hasher: &entryValueHasher)
result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize())
}
hasher.combine(result)
} else if let hashableValue = cleanValue as? AnyHashable {
hasher.combine(hashableValue)
} else {
hasher.combine(String(describing: cleanValue))
} }
return } else {
hasher.combine(0)
} }
if let hashableValue = value as? AnyHashable {
hasher.combine(hashableValue.hashValue)
}
return hasher.combine(String(describing: value))
} }
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct BackgroundWorkerSettings: Hashable { struct BackgroundWorkerSettings: Hashable {
@@ -137,9 +183,16 @@ struct BackgroundWorkerSettings: Hashable {
] ]
} }
static func == (lhs: BackgroundWorkerSettings, rhs: BackgroundWorkerSettings) -> Bool { static func == (lhs: BackgroundWorkerSettings, rhs: BackgroundWorkerSettings) -> Bool {
return deepEqualsBackgroundWorker(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsBackgroundWorker(lhs.requiresCharging, rhs.requiresCharging) && deepEqualsBackgroundWorker(lhs.minimumDelaySeconds, rhs.minimumDelaySeconds)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashBackgroundWorker(value: toList(), hasher: &hasher) hasher.combine("BackgroundWorkerSettings")
deepHashBackgroundWorker(value: requiresCharging, hasher: &hasher)
deepHashBackgroundWorker(value: minimumDelaySeconds, hasher: &hasher)
} }
} }
+2 -2
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -32,7 +32,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
+98 -36
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -32,7 +32,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
@@ -46,6 +46,19 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
return value as! T? return value as! T?
} }
private func doubleEqualsNetwork(_ lhs: Double, _ rhs: Double) -> Bool {
return (lhs.isNaN && rhs.isNaN) || lhs == rhs
}
private func doubleHashNetwork(_ value: Double, _ hasher: inout Hasher) {
if value.isNaN {
hasher.combine(0x7FF8000000000000)
} else {
// Normalize -0.0 to 0.0
hasher.combine(value == 0 ? 0 : value)
}
}
func deepEqualsNetwork(_ lhs: Any?, _ rhs: Any?) -> Bool { func deepEqualsNetwork(_ lhs: Any?, _ rhs: Any?) -> Bool {
let cleanLhs = nilOrValue(lhs) as Any? let cleanLhs = nilOrValue(lhs) as Any?
let cleanRhs = nilOrValue(rhs) as Any? let cleanRhs = nilOrValue(rhs) as Any?
@@ -56,59 +69,92 @@ func deepEqualsNetwork(_ lhs: Any?, _ rhs: Any?) -> Bool {
case (nil, _), (_, nil): case (nil, _), (_, nil):
return false return false
case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs:
return true
case is (Void, Void): case is (Void, Void):
return true return true
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): case (let lhsArray, let rhsArray) as ([Any?], [Any?]):
return cleanLhsHashable == cleanRhsHashable guard lhsArray.count == rhsArray.count else { return false }
for (index, element) in lhsArray.enumerated() {
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): if !deepEqualsNetwork(element, rhsArray[index]) {
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
for (index, element) in cleanLhsArray.enumerated() {
if !deepEqualsNetwork(element, cleanRhsArray[index]) {
return false return false
} }
} }
return true return true
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): case (let lhsArray, let rhsArray) as ([Double], [Double]):
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } guard lhsArray.count == rhsArray.count else { return false }
for (key, cleanLhsValue) in cleanLhsDictionary { for (index, element) in lhsArray.enumerated() {
guard cleanRhsDictionary.index(forKey: key) != nil else { return false } if !doubleEqualsNetwork(element, rhsArray[index]) {
if !deepEqualsNetwork(cleanLhsValue, cleanRhsDictionary[key]!) {
return false return false
} }
} }
return true return true
case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard lhsDictionary.count == rhsDictionary.count else { return false }
for (lhsKey, lhsValue) in lhsDictionary {
var found = false
for (rhsKey, rhsValue) in rhsDictionary {
if deepEqualsNetwork(lhsKey, rhsKey) {
if deepEqualsNetwork(lhsValue, rhsValue) {
found = true
break
} else {
return false
}
}
}
if !found { return false }
}
return true
case (let lhs as Double, let rhs as Double):
return doubleEqualsNetwork(lhs, rhs)
case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable):
return lhsHashable == rhsHashable
default: default:
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
return false return false
} }
} }
func deepHashNetwork(value: Any?, hasher: inout Hasher) { func deepHashNetwork(value: Any?, hasher: inout Hasher) {
if let valueList = value as? [AnyHashable] { let cleanValue = nilOrValue(value) as Any?
for item in valueList { deepHashNetwork(value: item, hasher: &hasher) } if let cleanValue = cleanValue {
return if let doubleValue = cleanValue as? Double {
} doubleHashNetwork(doubleValue, &hasher)
} else if let valueList = cleanValue as? [Any?] {
if let valueDict = value as? [AnyHashable: AnyHashable] { for item in valueList {
for key in valueDict.keys { deepHashNetwork(value: item, hasher: &hasher)
hasher.combine(key) }
deepHashNetwork(value: valueDict[key]!, hasher: &hasher) } else if let valueList = cleanValue as? [Double] {
for item in valueList {
doubleHashNetwork(item, &hasher)
}
} else if let valueDict = cleanValue as? [AnyHashable: Any?] {
var result = 0
for (key, value) in valueDict {
var entryKeyHasher = Hasher()
deepHashNetwork(value: key, hasher: &entryKeyHasher)
var entryValueHasher = Hasher()
deepHashNetwork(value: value, hasher: &entryValueHasher)
result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize())
}
hasher.combine(result)
} else if let hashableValue = cleanValue as? AnyHashable {
hasher.combine(hashableValue)
} else {
hasher.combine(String(describing: cleanValue))
} }
return } else {
hasher.combine(0)
} }
if let hashableValue = value as? AnyHashable {
hasher.combine(hashableValue.hashValue)
}
return hasher.combine(String(describing: value))
} }
/// Generated class from Pigeon that represents data sent in messages. /// Generated class from Pigeon that represents data sent in messages.
struct ClientCertData: Hashable { struct ClientCertData: Hashable {
@@ -133,9 +179,16 @@ struct ClientCertData: Hashable {
] ]
} }
static func == (lhs: ClientCertData, rhs: ClientCertData) -> Bool { static func == (lhs: ClientCertData, rhs: ClientCertData) -> Bool {
return deepEqualsNetwork(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsNetwork(lhs.data, rhs.data) && deepEqualsNetwork(lhs.password, rhs.password)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashNetwork(value: toList(), hasher: &hasher) hasher.combine("ClientCertData")
deepHashNetwork(value: data, hasher: &hasher)
deepHashNetwork(value: password, hasher: &hasher)
} }
} }
@@ -170,9 +223,18 @@ struct ClientCertPrompt: Hashable {
] ]
} }
static func == (lhs: ClientCertPrompt, rhs: ClientCertPrompt) -> Bool { static func == (lhs: ClientCertPrompt, rhs: ClientCertPrompt) -> Bool {
return deepEqualsNetwork(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsNetwork(lhs.title, rhs.title) && deepEqualsNetwork(lhs.message, rhs.message) && deepEqualsNetwork(lhs.cancel, rhs.cancel) && deepEqualsNetwork(lhs.confirm, rhs.confirm)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashNetwork(value: toList(), hasher: &hasher) hasher.combine("ClientCertPrompt")
deepHashNetwork(value: title, hasher: &hasher)
deepHashNetwork(value: message, hasher: &hasher)
deepHashNetwork(value: cancel, hasher: &hasher)
deepHashNetwork(value: confirm, hasher: &hasher)
} }
} }
+2 -2
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -32,7 +32,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
+2 -2
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -32,7 +32,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
+21 -21
View File
@@ -150,6 +150,27 @@
<array> <array>
<string>INSendMessageIntent</string> <string>INSendMessageIntent</string>
</array> </array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true/>
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
@@ -176,27 +197,6 @@
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<true/> <true/>
<key>io.flutter.embedded_views_preview</key> <key>io.flutter.embedded_views_preview</key>
+146 -46
View File
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v26.0.2), do not edit directly. // Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon // See also: https://pub.dev/packages/pigeon
import Foundation import Foundation
@@ -50,7 +50,7 @@ private func wrapError(_ error: Any) -> [Any?] {
} }
return [ return [
"\(error)", "\(error)",
"\(type(of: error))", "\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)", "Stacktrace: \(Thread.callStackSymbols)",
] ]
} }
@@ -64,6 +64,19 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
return value as! T? return value as! T?
} }
private func doubleEqualsMessages(_ lhs: Double, _ rhs: Double) -> Bool {
return (lhs.isNaN && rhs.isNaN) || lhs == rhs
}
private func doubleHashMessages(_ value: Double, _ hasher: inout Hasher) {
if value.isNaN {
hasher.combine(0x7FF8000000000000)
} else {
// Normalize -0.0 to 0.0
hasher.combine(value == 0 ? 0 : value)
}
}
func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool {
let cleanLhs = nilOrValue(lhs) as Any? let cleanLhs = nilOrValue(lhs) as Any?
let cleanRhs = nilOrValue(rhs) as Any? let cleanRhs = nilOrValue(rhs) as Any?
@@ -74,59 +87,92 @@ func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool {
case (nil, _), (_, nil): case (nil, _), (_, nil):
return false return false
case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs:
return true
case is (Void, Void): case is (Void, Void):
return true return true
case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): case (let lhsArray, let rhsArray) as ([Any?], [Any?]):
return cleanLhsHashable == cleanRhsHashable guard lhsArray.count == rhsArray.count else { return false }
for (index, element) in lhsArray.enumerated() {
case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): if !deepEqualsMessages(element, rhsArray[index]) {
guard cleanLhsArray.count == cleanRhsArray.count else { return false }
for (index, element) in cleanLhsArray.enumerated() {
if !deepEqualsMessages(element, cleanRhsArray[index]) {
return false return false
} }
} }
return true return true
case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): case (let lhsArray, let rhsArray) as ([Double], [Double]):
guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } guard lhsArray.count == rhsArray.count else { return false }
for (key, cleanLhsValue) in cleanLhsDictionary { for (index, element) in lhsArray.enumerated() {
guard cleanRhsDictionary.index(forKey: key) != nil else { return false } if !doubleEqualsMessages(element, rhsArray[index]) {
if !deepEqualsMessages(cleanLhsValue, cleanRhsDictionary[key]!) {
return false return false
} }
} }
return true return true
case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]):
guard lhsDictionary.count == rhsDictionary.count else { return false }
for (lhsKey, lhsValue) in lhsDictionary {
var found = false
for (rhsKey, rhsValue) in rhsDictionary {
if deepEqualsMessages(lhsKey, rhsKey) {
if deepEqualsMessages(lhsValue, rhsValue) {
found = true
break
} else {
return false
}
}
}
if !found { return false }
}
return true
case (let lhs as Double, let rhs as Double):
return doubleEqualsMessages(lhs, rhs)
case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable):
return lhsHashable == rhsHashable
default: default:
// Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue.
return false return false
} }
} }
func deepHashMessages(value: Any?, hasher: inout Hasher) { func deepHashMessages(value: Any?, hasher: inout Hasher) {
if let valueList = value as? [AnyHashable] { let cleanValue = nilOrValue(value) as Any?
for item in valueList { deepHashMessages(value: item, hasher: &hasher) } if let cleanValue = cleanValue {
return if let doubleValue = cleanValue as? Double {
} doubleHashMessages(doubleValue, &hasher)
} else if let valueList = cleanValue as? [Any?] {
if let valueDict = value as? [AnyHashable: AnyHashable] { for item in valueList {
for key in valueDict.keys { deepHashMessages(value: item, hasher: &hasher)
hasher.combine(key) }
deepHashMessages(value: valueDict[key]!, hasher: &hasher) } else if let valueList = cleanValue as? [Double] {
for item in valueList {
doubleHashMessages(item, &hasher)
}
} else if let valueDict = cleanValue as? [AnyHashable: Any?] {
var result = 0
for (key, value) in valueDict {
var entryKeyHasher = Hasher()
deepHashMessages(value: key, hasher: &entryKeyHasher)
var entryValueHasher = Hasher()
deepHashMessages(value: value, hasher: &entryValueHasher)
result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize())
}
hasher.combine(result)
} else if let hashableValue = cleanValue as? AnyHashable {
hasher.combine(hashableValue)
} else {
hasher.combine(String(describing: cleanValue))
} }
return } else {
hasher.combine(0)
} }
if let hashableValue = value as? AnyHashable {
hasher.combine(hashableValue.hashValue)
}
return hasher.combine(String(describing: value))
} }
enum PlatformAssetPlaybackStyle: Int { enum PlatformAssetPlaybackStyle: Int {
case unknown = 0 case unknown = 0
@@ -146,7 +192,7 @@ struct PlatformAsset: Hashable {
var updatedAt: Int64? = nil var updatedAt: Int64? = nil
var width: Int64? = nil var width: Int64? = nil
var height: Int64? = nil var height: Int64? = nil
var durationInSeconds: Int64 var durationMs: Int64
var orientation: Int64 var orientation: Int64
var isFavorite: Bool var isFavorite: Bool
var adjustmentTime: Int64? = nil var adjustmentTime: Int64? = nil
@@ -164,7 +210,7 @@ struct PlatformAsset: Hashable {
let updatedAt: Int64? = nilOrValue(pigeonVar_list[4]) let updatedAt: Int64? = nilOrValue(pigeonVar_list[4])
let width: Int64? = nilOrValue(pigeonVar_list[5]) let width: Int64? = nilOrValue(pigeonVar_list[5])
let height: Int64? = nilOrValue(pigeonVar_list[6]) let height: Int64? = nilOrValue(pigeonVar_list[6])
let durationInSeconds = pigeonVar_list[7] as! Int64 let durationMs = pigeonVar_list[7] as! Int64
let orientation = pigeonVar_list[8] as! Int64 let orientation = pigeonVar_list[8] as! Int64
let isFavorite = pigeonVar_list[9] as! Bool let isFavorite = pigeonVar_list[9] as! Bool
let adjustmentTime: Int64? = nilOrValue(pigeonVar_list[10]) let adjustmentTime: Int64? = nilOrValue(pigeonVar_list[10])
@@ -180,7 +226,7 @@ struct PlatformAsset: Hashable {
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
orientation: orientation, orientation: orientation,
isFavorite: isFavorite, isFavorite: isFavorite,
adjustmentTime: adjustmentTime, adjustmentTime: adjustmentTime,
@@ -198,7 +244,7 @@ struct PlatformAsset: Hashable {
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
orientation, orientation,
isFavorite, isFavorite,
adjustmentTime, adjustmentTime,
@@ -208,9 +254,28 @@ struct PlatformAsset: Hashable {
] ]
} }
static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool { static func == (lhs: PlatformAsset, rhs: PlatformAsset) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsMessages(lhs.id, rhs.id) && deepEqualsMessages(lhs.name, rhs.name) && deepEqualsMessages(lhs.type, rhs.type) && deepEqualsMessages(lhs.createdAt, rhs.createdAt) && deepEqualsMessages(lhs.updatedAt, rhs.updatedAt) && deepEqualsMessages(lhs.width, rhs.width) && deepEqualsMessages(lhs.height, rhs.height) && deepEqualsMessages(lhs.durationMs, rhs.durationMs) && deepEqualsMessages(lhs.orientation, rhs.orientation) && deepEqualsMessages(lhs.isFavorite, rhs.isFavorite) && deepEqualsMessages(lhs.adjustmentTime, rhs.adjustmentTime) && deepEqualsMessages(lhs.latitude, rhs.latitude) && deepEqualsMessages(lhs.longitude, rhs.longitude) && deepEqualsMessages(lhs.playbackStyle, rhs.playbackStyle)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher) hasher.combine("PlatformAsset")
deepHashMessages(value: id, hasher: &hasher)
deepHashMessages(value: name, hasher: &hasher)
deepHashMessages(value: type, hasher: &hasher)
deepHashMessages(value: createdAt, hasher: &hasher)
deepHashMessages(value: updatedAt, hasher: &hasher)
deepHashMessages(value: width, hasher: &hasher)
deepHashMessages(value: height, hasher: &hasher)
deepHashMessages(value: durationMs, hasher: &hasher)
deepHashMessages(value: orientation, hasher: &hasher)
deepHashMessages(value: isFavorite, hasher: &hasher)
deepHashMessages(value: adjustmentTime, hasher: &hasher)
deepHashMessages(value: latitude, hasher: &hasher)
deepHashMessages(value: longitude, hasher: &hasher)
deepHashMessages(value: playbackStyle, hasher: &hasher)
} }
} }
@@ -249,9 +314,19 @@ struct PlatformAlbum: Hashable {
] ]
} }
static func == (lhs: PlatformAlbum, rhs: PlatformAlbum) -> Bool { static func == (lhs: PlatformAlbum, rhs: PlatformAlbum) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsMessages(lhs.id, rhs.id) && deepEqualsMessages(lhs.name, rhs.name) && deepEqualsMessages(lhs.updatedAt, rhs.updatedAt) && deepEqualsMessages(lhs.isCloud, rhs.isCloud) && deepEqualsMessages(lhs.assetCount, rhs.assetCount)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher) hasher.combine("PlatformAlbum")
deepHashMessages(value: id, hasher: &hasher)
deepHashMessages(value: name, hasher: &hasher)
deepHashMessages(value: updatedAt, hasher: &hasher)
deepHashMessages(value: isCloud, hasher: &hasher)
deepHashMessages(value: assetCount, hasher: &hasher)
} }
} }
@@ -286,9 +361,18 @@ struct SyncDelta: Hashable {
] ]
} }
static func == (lhs: SyncDelta, rhs: SyncDelta) -> Bool { static func == (lhs: SyncDelta, rhs: SyncDelta) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsMessages(lhs.hasChanges, rhs.hasChanges) && deepEqualsMessages(lhs.updates, rhs.updates) && deepEqualsMessages(lhs.deletes, rhs.deletes) && deepEqualsMessages(lhs.assetAlbums, rhs.assetAlbums)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher) hasher.combine("SyncDelta")
deepHashMessages(value: hasChanges, hasher: &hasher)
deepHashMessages(value: updates, hasher: &hasher)
deepHashMessages(value: deletes, hasher: &hasher)
deepHashMessages(value: assetAlbums, hasher: &hasher)
} }
} }
@@ -319,9 +403,17 @@ struct HashResult: Hashable {
] ]
} }
static func == (lhs: HashResult, rhs: HashResult) -> Bool { static func == (lhs: HashResult, rhs: HashResult) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsMessages(lhs.assetId, rhs.assetId) && deepEqualsMessages(lhs.error, rhs.error) && deepEqualsMessages(lhs.hash, rhs.hash)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher) hasher.combine("HashResult")
deepHashMessages(value: assetId, hasher: &hasher)
deepHashMessages(value: error, hasher: &hasher)
deepHashMessages(value: hash, hasher: &hasher)
} }
} }
@@ -352,9 +444,17 @@ struct CloudIdResult: Hashable {
] ]
} }
static func == (lhs: CloudIdResult, rhs: CloudIdResult) -> Bool { static func == (lhs: CloudIdResult, rhs: CloudIdResult) -> Bool {
return deepEqualsMessages(lhs.toList(), rhs.toList()) } if Swift.type(of: lhs) != Swift.type(of: rhs) {
return false
}
return deepEqualsMessages(lhs.assetId, rhs.assetId) && deepEqualsMessages(lhs.error, rhs.error) && deepEqualsMessages(lhs.cloudId, rhs.cloudId)
}
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
deepHashMessages(value: toList(), hasher: &hasher) hasher.combine("CloudIdResult")
deepHashMessages(value: assetId, hasher: &hasher)
deepHashMessages(value: error, hasher: &hasher)
deepHashMessages(value: cloudId, hasher: &hasher)
} }
} }
+1 -1
View File
@@ -171,7 +171,7 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
id: asset.localIdentifier, id: asset.localIdentifier,
name: "", name: "",
type: 0, type: 0,
durationInSeconds: 0, durationMs: 0,
orientation: 0, orientation: 0,
isFavorite: false, isFavorite: false,
playbackStyle: .unknown playbackStyle: .unknown
@@ -21,7 +21,7 @@ extension PHAsset {
updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) }, updatedAt: modificationDate.map { Int64($0.timeIntervalSince1970) },
width: Int64(pixelWidth), width: Int64(pixelWidth),
height: Int64(pixelHeight), height: Int64(pixelHeight),
durationInSeconds: Int64(duration), durationMs: Int64(duration * 1000),
orientation: 0, orientation: 0,
isFavorite: isFavorite, isFavorite: isFavorite,
adjustmentTime: adjustmentTimestamp, adjustmentTime: adjustmentTimestamp,
+26 -26
View File
@@ -1,35 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>AppGroupId</key> <key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string> <string>$(CUSTOM_GROUP_ID)</string>
<key>NSExtension</key> <key>NSExtension</key>
<dict> <dict>
<key>NSExtensionAttributes</key> <key>NSExtensionAttributes</key>
<dict> <dict>
<key>IntentsSupported</key> <key>IntentsSupported</key>
<array> <array>
<string>INSendMessageIntent</string> <string>INSendMessageIntent</string>
</array> </array>
<key>NSExtensionActivationRule</key> <key>NSExtensionActivationRule</key>
<string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments, <string>SUBQUERY ( extensionItems, $extensionItem, SUBQUERY ( $extensionItem.attachments,
$attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url" $attachment, ( ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.file-url"
|| ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.movie" || ANY
$attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count &gt; 0 $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" ) ).@count &gt; 0
).@count &gt; 0 </string> ).@count &gt; 0 </string>
<key>PHSupportedMediaTypes</key> <key>PHSupportedMediaTypes</key>
<array> <array>
<string>Video</string> <string>Video</string>
<string>Image</string> <string>Image</string>
</array> </array>
</dict> </dict>
<key>NSExtensionMainStoryboard</key> <key>NSExtensionMainStoryboard</key>
<string>MainInterface</string> <string>MainInterface</string>
<key>NSExtensionPointIdentifier</key> <key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string> <string>com.apple.share-services</string>
</dict> </dict>
</dict> </dict>
</plist> </plist>
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.app.immich.share</string> <string>group.app.immich.share</string>
</array> </array>
</dict> </dict>
</plist> </plist>
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.application-groups</key> <key>com.apple.security.application-groups</key>
<array> <array>
<string>group.app.immich.share</string> <string>group.app.immich.share</string>
</array> </array>
</dict> </dict>
</plist> </plist>
@@ -8,6 +8,7 @@ enum AlbumUserRole {
// do not change this order! // do not change this order!
editor, editor,
viewer, viewer,
owner,
} }
// Model for an album stored in the server // Model for an album stored in the server
@@ -25,7 +25,7 @@ sealed class BaseAsset {
final DateTime updatedAt; final DateTime updatedAt;
final int? width; final int? width;
final int? height; final int? height;
final int? durationInSeconds; final int? durationMs;
final bool isFavorite; final bool isFavorite;
final String? livePhotoVideoId; final String? livePhotoVideoId;
final bool isEdited; final bool isEdited;
@@ -38,7 +38,7 @@ sealed class BaseAsset {
required this.updatedAt, required this.updatedAt,
this.width, this.width,
this.height, this.height,
this.durationInSeconds, this.durationMs,
this.isFavorite = false, this.isFavorite = false,
this.livePhotoVideoId, this.livePhotoVideoId,
required this.isEdited, required this.isEdited,
@@ -53,15 +53,17 @@ sealed class BaseAsset {
AssetPlaybackStyle get playbackStyle { AssetPlaybackStyle get playbackStyle {
if (isVideo) return AssetPlaybackStyle.video; if (isVideo) return AssetPlaybackStyle.video;
if (isMotionPhoto) return AssetPlaybackStyle.livePhoto; if (isMotionPhoto) return AssetPlaybackStyle.livePhoto;
if (isImage && durationInSeconds != null && durationInSeconds! > 0) return AssetPlaybackStyle.imageAnimated; if (isImage && durationMs != null && durationMs! > 0) {
return AssetPlaybackStyle.imageAnimated;
}
if (isImage) return AssetPlaybackStyle.image; if (isImage) return AssetPlaybackStyle.image;
return AssetPlaybackStyle.unknown; return AssetPlaybackStyle.unknown;
} }
Duration get duration { Duration get duration {
final durationInSeconds = this.durationInSeconds; final durationMs = this.durationMs;
if (durationInSeconds != null) { if (durationMs != null) {
return Duration(seconds: durationInSeconds); return Duration(milliseconds: durationMs);
} }
return const Duration(); return const Duration();
} }
@@ -88,7 +90,7 @@ sealed class BaseAsset {
updatedAt: $updatedAt, updatedAt: $updatedAt,
width: ${width ?? "<NA>"}, width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"}, height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}, durationMs: ${durationMs ?? "<NA>"},
isFavorite: $isFavorite, isFavorite: $isFavorite,
isEdited: $isEdited, isEdited: $isEdited,
}'''; }''';
@@ -104,7 +106,7 @@ sealed class BaseAsset {
updatedAt == other.updatedAt && updatedAt == other.updatedAt &&
width == other.width && width == other.width &&
height == other.height && height == other.height &&
durationInSeconds == other.durationInSeconds && durationMs == other.durationMs &&
isFavorite == other.isFavorite && isFavorite == other.isFavorite &&
isEdited == other.isEdited; isEdited == other.isEdited;
} }
@@ -119,7 +121,7 @@ sealed class BaseAsset {
updatedAt.hashCode ^ updatedAt.hashCode ^
width.hashCode ^ width.hashCode ^
height.hashCode ^ height.hashCode ^
durationInSeconds.hashCode ^ durationMs.hashCode ^
isFavorite.hashCode ^ isFavorite.hashCode ^
isEdited.hashCode; isEdited.hashCode;
} }
@@ -23,7 +23,7 @@ class LocalAsset extends BaseAsset {
required super.updatedAt, required super.updatedAt,
super.width, super.width,
super.height, super.height,
super.durationInSeconds, super.durationMs,
super.isFavorite = false, super.isFavorite = false,
super.livePhotoVideoId, super.livePhotoVideoId,
this.orientation = 0, this.orientation = 0,
@@ -58,7 +58,7 @@ class LocalAsset extends BaseAsset {
updatedAt: $updatedAt, updatedAt: $updatedAt,
width: ${width ?? "<NA>"}, width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"}, height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}, durationMs: ${durationMs ?? "<NA>"},
playbackStyle: $playbackStyle, playbackStyle: $playbackStyle,
remoteId: ${remoteId ?? "<NA>"}, remoteId: ${remoteId ?? "<NA>"},
cloudId: ${cloudId ?? "<NA>"}, cloudId: ${cloudId ?? "<NA>"},
@@ -108,7 +108,7 @@ class LocalAsset extends BaseAsset {
DateTime? updatedAt, DateTime? updatedAt,
int? width, int? width,
int? height, int? height,
int? durationInSeconds, int? durationMs,
bool? isFavorite, bool? isFavorite,
int? orientation, int? orientation,
AssetPlaybackStyle? playbackStyle, AssetPlaybackStyle? playbackStyle,
@@ -128,7 +128,7 @@ class LocalAsset extends BaseAsset {
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
orientation: orientation ?? this.orientation, orientation: orientation ?? this.orientation,
playbackStyle: playbackStyle ?? this.playbackStyle, playbackStyle: playbackStyle ?? this.playbackStyle,
@@ -22,7 +22,7 @@ class RemoteAsset extends BaseAsset {
required super.updatedAt, required super.updatedAt,
super.width, super.width,
super.height, super.height,
super.durationInSeconds, super.durationMs,
super.isFavorite = false, super.isFavorite = false,
this.thumbHash, this.thumbHash,
this.visibility = AssetVisibility.timeline, this.visibility = AssetVisibility.timeline,
@@ -57,7 +57,7 @@ class RemoteAsset extends BaseAsset {
updatedAt: $updatedAt, updatedAt: $updatedAt,
width: ${width ?? "<NA>"}, width: ${width ?? "<NA>"},
height: ${height ?? "<NA>"}, height: ${height ?? "<NA>"},
durationInSeconds: ${durationInSeconds ?? "<NA>"}, durationMs: ${durationMs ?? "<NA>"},
localId: ${localId ?? "<NA>"}, localId: ${localId ?? "<NA>"},
isFavorite: $isFavorite, isFavorite: $isFavorite,
thumbHash: ${thumbHash ?? "<NA>"}, thumbHash: ${thumbHash ?? "<NA>"},
@@ -102,7 +102,7 @@ class RemoteAsset extends BaseAsset {
DateTime? updatedAt, DateTime? updatedAt,
int? width, int? width,
int? height, int? height,
int? durationInSeconds, int? durationMs,
bool? isFavorite, bool? isFavorite,
String? thumbHash, String? thumbHash,
AssetVisibility? visibility, AssetVisibility? visibility,
@@ -121,7 +121,7 @@ class RemoteAsset extends BaseAsset {
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
thumbHash: thumbHash ?? this.thumbHash, thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility, visibility: visibility ?? this.visibility,
@@ -146,7 +146,7 @@ class RemoteAssetExif extends RemoteAsset {
required super.updatedAt, required super.updatedAt,
super.width, super.width,
super.height, super.height,
super.durationInSeconds, super.durationMs,
super.isFavorite = false, super.isFavorite = false,
super.thumbHash, super.thumbHash,
super.visibility = AssetVisibility.timeline, super.visibility = AssetVisibility.timeline,
@@ -178,7 +178,7 @@ class RemoteAssetExif extends RemoteAsset {
DateTime? updatedAt, DateTime? updatedAt,
int? width, int? width,
int? height, int? height,
int? durationInSeconds, int? durationMs,
bool? isFavorite, bool? isFavorite,
String? thumbHash, String? thumbHash,
AssetVisibility? visibility, AssetVisibility? visibility,
@@ -198,7 +198,7 @@ class RemoteAssetExif extends RemoteAsset {
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
thumbHash: thumbHash ?? this.thumbHash, thumbHash: thumbHash ?? this.thumbHash,
visibility: visibility ?? this.visibility, visibility: visibility ?? this.visibility,
+1 -1
View File
@@ -29,7 +29,7 @@ class ExifInfo {
bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0; bool get hasCoordinates => latitude != null && longitude != null && latitude != 0 && longitude != 0;
String get exposureTime { String get exposureTime {
if (exposureSeconds == null) { if (exposureSeconds == null || exposureSeconds! <= 0 || exposureSeconds!.isNaN) {
return ""; return "";
} }
if (exposureSeconds! < 1) { if (exposureSeconds! < 1) {
@@ -337,7 +337,7 @@ class LocalSyncService {
a.createdAt.isAtSameMomentAs(b.createdAt) && a.createdAt.isAtSameMomentAs(b.createdAt) &&
a.width == b.width && a.width == b.width &&
a.height == b.height && a.height == b.height &&
a.durationInSeconds == b.durationInSeconds; a.durationMs == b.durationMs;
} }
final firstAdjustment = a.adjustmentTime?.millisecondsSinceEpoch ?? 0; final firstAdjustment = a.adjustmentTime?.millisecondsSinceEpoch ?? 0;
@@ -346,7 +346,7 @@ class LocalSyncService {
a.createdAt.isAtSameMomentAs(b.createdAt) && a.createdAt.isAtSameMomentAs(b.createdAt) &&
a.width == b.width && a.width == b.width &&
a.height == b.height && a.height == b.height &&
a.durationInSeconds == b.durationInSeconds && a.durationMs == b.durationMs &&
a.latitude == b.latitude && a.latitude == b.latitude &&
a.longitude == b.longitude; a.longitude == b.longitude;
} }
@@ -432,7 +432,7 @@ extension PlatformToLocalAsset on PlatformAsset {
updatedAt: tryFromSecondsSinceEpoch(updatedAt, isUtc: true) ?? DateTime.timestamp(), updatedAt: tryFromSecondsSinceEpoch(updatedAt, isUtc: true) ?? DateTime.timestamp(),
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
isFavorite: isFavorite, isFavorite: isFavorite,
orientation: orientation, orientation: orientation,
playbackStyle: _toPlaybackStyle(playbackStyle), playbackStyle: _toPlaybackStyle(playbackStyle),
@@ -7,8 +7,8 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart'; import 'package:immich_mobile/providers/album/album_sort_by_options.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
class RemoteAlbumService { class RemoteAlbumService {
final DriftRemoteAlbumRepository _repository; final DriftRemoteAlbumRepository _repository;
@@ -28,10 +28,6 @@ class RemoteAlbumService {
return _repository.get(albumId); return _repository.get(albumId);
} }
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
return _repository.getByName(albumName, ownerId);
}
Future<List<RemoteAlbum>> sortAlbums( Future<List<RemoteAlbum>> sortAlbums(
List<RemoteAlbum> albums, List<RemoteAlbum> albums,
AlbumSortMode sortMode, { AlbumSortMode sortMode, {
@@ -86,8 +82,18 @@ class RemoteAlbumService {
return filtered; return filtered;
} }
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async { Future<RemoteAlbum> createAlbum({
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds); required String title,
required UserDto owner,
required List<String> assetIds,
String? description,
}) async {
final album = await _albumApiRepository.createDriftAlbum(
title,
owner,
description: description,
assetIds: assetIds,
);
await _repository.create(album, assetIds); await _repository.create(album, assetIds);
return album; return album;
@@ -101,8 +107,10 @@ class RemoteAlbumService {
bool? isActivityEnabled, bool? isActivityEnabled,
AlbumAssetOrder? order, AlbumAssetOrder? order,
}) async { }) async {
final owner = await _repository.getOwner(albumId);
final updatedAlbum = await _albumApiRepository.updateAlbum( final updatedAlbum = await _albumApiRepository.updateAlbum(
albumId, albumId,
owner,
name: name, name: name,
description: description, description: description,
thumbnailAssetId: thumbnailAssetId, thumbnailAssetId: thumbnailAssetId,
@@ -1,8 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/album/local_album.model.dart'; import 'package:immich_mobile/domain/models/album/local_album.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/services/store.service.dart';
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/utils/debug_print.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
@@ -12,6 +15,7 @@ final syncLinkedAlbumServiceProvider = Provider(
ref.watch(localAlbumRepository), ref.watch(localAlbumRepository),
ref.watch(remoteAlbumRepository), ref.watch(remoteAlbumRepository),
ref.watch(driftAlbumApiRepositoryProvider), ref.watch(driftAlbumApiRepositoryProvider),
ref.watch(storeServiceProvider),
), ),
); );
@@ -19,8 +23,14 @@ class SyncLinkedAlbumService {
final DriftLocalAlbumRepository _localAlbumRepository; final DriftLocalAlbumRepository _localAlbumRepository;
final DriftRemoteAlbumRepository _remoteAlbumRepository; final DriftRemoteAlbumRepository _remoteAlbumRepository;
final DriftAlbumApiRepository _albumApiRepository; final DriftAlbumApiRepository _albumApiRepository;
final StoreService _storeService;
SyncLinkedAlbumService(this._localAlbumRepository, this._remoteAlbumRepository, this._albumApiRepository); SyncLinkedAlbumService(
this._localAlbumRepository,
this._remoteAlbumRepository,
this._albumApiRepository,
this._storeService,
);
final _log = Logger("SyncLinkedAlbumService"); final _log = Logger("SyncLinkedAlbumService");
@@ -103,7 +113,11 @@ class SyncLinkedAlbumService {
/// Creates a new remote album and links it to the local album /// Creates a new remote album and links it to the local album
Future<void> _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async { Future<void> _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async {
dPrint(() => "Creating new remote album for local album: ${localAlbum.name}"); dPrint(() => "Creating new remote album for local album: ${localAlbum.name}");
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, assetIds: []); final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(
localAlbum.name,
_storeService.get(StoreKey.currentUser),
assetIds: [],
);
await _remoteAlbumRepository.create(newRemoteAlbum, []); await _remoteAlbumRepository.create(newRemoteAlbum, []);
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id); return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id);
} }
@@ -3,6 +3,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/store.model.dart';
import 'package:immich_mobile/domain/models/sync_event.model.dart'; import 'package:immich_mobile/domain/models/sync_event.model.dart';
import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart';
@@ -191,17 +192,22 @@ class SyncStreamService {
case SyncEntityType.assetV1: case SyncEntityType.assetV1:
final remoteSyncAssets = data.cast<SyncAssetV1>(); final remoteSyncAssets = data.cast<SyncAssetV1>();
await _syncStreamRepository.updateAssetsV1(remoteSyncAssets); await _syncStreamRepository.updateAssetsV1(remoteSyncAssets);
if (CurrentPlatform.isAndroid && Store.get(StoreKey.manageLocalMediaAndroid, false)) { await _runWithManageMediaPermission(
final hasPermission = await _localFilesManager.hasManageMediaPermission(); logContext: "Trashed Assets",
if (hasPermission) { action: () async {
await _handleRemoteTrashed(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.checksum)); await _handleRemoteDeleted(remoteSyncAssets.where((e) => e.deletedAt != null).map((e) => e.id));
await _applyRemoteRestoreToLocal(); await _applyRemoteRestoreToLocal();
} else { },
_logger.warning("sync Trashed Assets cannot proceed because MANAGE_MEDIA permission is missing"); );
}
}
return; return;
case SyncEntityType.assetDeleteV1: case SyncEntityType.assetDeleteV1:
await _runWithManageMediaPermission(
logContext: "Deleted Assets",
action: () async {
final remoteSyncAssets = data.cast<SyncAssetDeleteV1>();
await _handleRemoteDeleted(remoteSyncAssets.map((e) => e.assetId));
},
);
return _syncStreamRepository.deleteAssetsV1(data.cast()); return _syncStreamRepository.deleteAssetsV1(data.cast());
case SyncEntityType.assetExifV1: case SyncEntityType.assetExifV1:
return _syncStreamRepository.updateAssetsExifV1(data.cast()); return _syncStreamRepository.updateAssetsExifV1(data.cast());
@@ -225,6 +231,8 @@ class SyncStreamService {
return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill'); return _syncStreamRepository.updateAssetsExifV1(data.cast(), debugLabel: 'partner backfill');
case SyncEntityType.albumV1: case SyncEntityType.albumV1:
return _syncStreamRepository.updateAlbumsV1(data.cast()); return _syncStreamRepository.updateAlbumsV1(data.cast());
case SyncEntityType.albumV2:
return _syncStreamRepository.updateAlbumsV2(data.cast());
case SyncEntityType.albumDeleteV1: case SyncEntityType.albumDeleteV1:
return _syncStreamRepository.deleteAlbumsV1(data.cast()); return _syncStreamRepository.deleteAlbumsV1(data.cast());
case SyncEntityType.albumUserV1: case SyncEntityType.albumUserV1:
@@ -380,28 +388,32 @@ class SyncStreamService {
} }
} }
Future<void> _handleRemoteTrashed(Iterable<String> checksums) async { Future<void> _handleRemoteDeleted(Iterable<String> remoteIds) async {
if (checksums.isEmpty) { if (remoteIds.isEmpty) {
return Future.value(); return Future.value();
} else { } else {
final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(checksums); final localAssetsToTrash = await _localAssetRepository.getAssetsFromBackupAlbums(remoteIds);
if (localAssetsToTrash.isNotEmpty) { if (localAssetsToTrash.isNotEmpty) {
final mediaUrls = await Future.wait( await _trashLocalAssets(localAssetsToTrash);
localAssetsToTrash.values
.expand((e) => e)
.map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
);
_logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
if (result) {
await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
}
} else { } else {
_logger.info("No assets found in backup-enabled albums for assets: $checksums"); _logger.info("No assets found in backup-enabled albums for remote assets: $remoteIds");
} }
} }
} }
Future<void> _trashLocalAssets(Map<String, List<LocalAsset>> localAssetsToTrash) async {
final mediaUrls = await Future.wait(
localAssetsToTrash.values
.expand((e) => e)
.map((localAsset) => _storageRepository.getAssetEntityForAsset(localAsset).then((e) => e?.getMediaUrl())),
);
_logger.info("Moving to trash ${mediaUrls.join(", ")} assets");
final result = await _localFilesManager.moveToTrash(mediaUrls.nonNulls.toList());
if (result) {
await _trashedLocalAssetRepository.trashLocalAsset(localAssetsToTrash);
}
}
Future<void> _applyRemoteRestoreToLocal() async { Future<void> _applyRemoteRestoreToLocal() async {
final assetsToRestore = await _trashedLocalAssetRepository.getToRestore(); final assetsToRestore = await _trashedLocalAssetRepository.getToRestore();
if (assetsToRestore.isNotEmpty) { if (assetsToRestore.isNotEmpty) {
@@ -411,4 +423,21 @@ class SyncStreamService {
_logger.info("No remote assets found for restoration"); _logger.info("No remote assets found for restoration");
} }
} }
Future<void> _runWithManageMediaPermission({
required String logContext,
required Future<void> Function() action,
}) async {
if (!CurrentPlatform.isAndroid || !Store.get(StoreKey.manageLocalMediaAndroid, false)) {
return;
}
final hasPermission = await _localFilesManager.hasManageMediaPermission();
if (!hasPermission) {
_logger.warning("sync $logContext cannot proceed because MANAGE_MEDIA permission is missing");
return;
}
await action();
}
} }
+2 -2
View File
@@ -14,7 +14,7 @@ extension DTOToAsset on api.AssetResponseDto {
updatedAt: updatedAt, updatedAt: updatedAt,
ownerId: ownerId, ownerId: ownerId,
visibility: visibility.toAssetVisibility(), visibility: visibility.toAssetVisibility(),
durationInSeconds: duration?.toDuration()?.inSeconds ?? 0, durationMs: duration?.toDuration()?.inMilliseconds ?? 0,
height: height?.toInt(), height: height?.toInt(),
width: width?.toInt(), width: width?.toInt(),
isFavorite: isFavorite, isFavorite: isFavorite,
@@ -36,7 +36,7 @@ extension DTOToAsset on api.AssetResponseDto {
updatedAt: updatedAt, updatedAt: updatedAt,
ownerId: ownerId, ownerId: ownerId,
visibility: visibility.toAssetVisibility(), visibility: visibility.toAssetVisibility(),
durationInSeconds: duration?.toDuration()?.inSeconds ?? 0, durationMs: duration?.toDuration()?.inMilliseconds ?? 0,
height: height?.toInt(), height: height?.toInt(),
width: width?.toInt(), width: width?.toInt(),
isFavorite: isFavorite, isFavorite: isFavorite,
+8 -3
View File
@@ -7,11 +7,16 @@ extension StringExtension on String {
} }
extension DurationExtension on String { extension DurationExtension on String {
/// Parses and returns the string of format HH:MM:SS as a duration object else null /// Parses and returns the string of format HH:MM:SS.ffffff as a duration object else null
Duration? toDuration() { Duration? toDuration() {
try { try {
final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false); final parts = split(':');
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]); final hours = double.parse(parts[0]).toInt();
final minutes = double.parse(parts[1]).toInt();
final secondsParts = parts[2].split('.');
final seconds = int.parse(secondsParts[0]);
final milliseconds = secondsParts.length > 1 ? (double.parse('0.${secondsParts[1]}') * 1000).round() : 0;
return Duration(hours: hours, minutes: minutes, seconds: seconds, milliseconds: milliseconds);
} catch (e) { } catch (e) {
return null; return null;
} }
@@ -39,7 +39,7 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData {
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
durationInSeconds: durationInSeconds, durationMs: durationMs,
isFavorite: isFavorite, isFavorite: isFavorite,
height: height, height: height,
width: width, width: width,
@@ -16,7 +16,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
required String id, required String id,
i0.Value<String?> checksum, i0.Value<String?> checksum,
i0.Value<bool> isFavorite, i0.Value<bool> isFavorite,
@@ -35,7 +35,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
i0.Value<String> id, i0.Value<String> id,
i0.Value<String?> checksum, i0.Value<String?> checksum,
i0.Value<bool> isFavorite, i0.Value<bool> isFavorite,
@@ -87,8 +87,8 @@ class $$LocalAssetEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder( i0.ColumnFilters<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
@@ -182,8 +182,8 @@ class $$LocalAssetEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder( i0.ColumnOrderings<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
@@ -260,8 +260,8 @@ class $$LocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<int> get height => i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column); $composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder( i0.GeneratedColumn<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => column, builder: (column) => column,
); );
@@ -348,7 +348,7 @@ class $$LocalAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(), i0.Value<String> id = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(), i0.Value<bool> isFavorite = const i0.Value.absent(),
@@ -366,7 +366,7 @@ class $$LocalAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
checksum: checksum, checksum: checksum,
isFavorite: isFavorite, isFavorite: isFavorite,
@@ -385,7 +385,7 @@ class $$LocalAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
required String id, required String id,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(), i0.Value<bool> isFavorite = const i0.Value.absent(),
@@ -403,7 +403,7 @@ class $$LocalAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
checksum: checksum, checksum: checksum,
isFavorite: isFavorite, isFavorite: isFavorite,
@@ -522,17 +522,17 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
type: i0.DriftSqlType.int, type: i0.DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
); );
static const i0.VerificationMeta _durationInSecondsMeta = static const i0.VerificationMeta _durationMsMeta = const i0.VerificationMeta(
const i0.VerificationMeta('durationInSeconds'); 'durationMs',
);
@override @override
late final i0.GeneratedColumn<int> durationInSeconds = late final i0.GeneratedColumn<int> durationMs = i0.GeneratedColumn<int>(
i0.GeneratedColumn<int>( 'duration_ms',
'duration_in_seconds', aliasedName,
aliasedName, true,
true, type: i0.DriftSqlType.int,
type: i0.DriftSqlType.int, requiredDuringInsert: false,
requiredDuringInsert: false, );
);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override @override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>( late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
@@ -645,7 +645,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
checksum, checksum,
isFavorite, isFavorite,
@@ -700,13 +700,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
height.isAcceptableOrUnknown(data['height']!, _heightMeta), height.isAcceptableOrUnknown(data['height']!, _heightMeta),
); );
} }
if (data.containsKey('duration_in_seconds')) { if (data.containsKey('duration_ms')) {
context.handle( context.handle(
_durationInSecondsMeta, _durationMsMeta,
durationInSeconds.isAcceptableOrUnknown( durationMs.isAcceptableOrUnknown(data['duration_ms']!, _durationMsMeta),
data['duration_in_seconds']!,
_durationInSecondsMeta,
),
); );
} }
if (data.containsKey('id')) { if (data.containsKey('id')) {
@@ -800,9 +797,9 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}height'], data['${effectivePrefix}height'],
), ),
durationInSeconds: attachedDatabase.typeMapping.read( durationMs: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}duration_in_seconds'], data['${effectivePrefix}duration_ms'],
), ),
id: attachedDatabase.typeMapping.read( id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, i0.DriftSqlType.string,
@@ -870,7 +867,7 @@ class LocalAssetEntityData extends i0.DataClass
final DateTime updatedAt; final DateTime updatedAt;
final int? width; final int? width;
final int? height; final int? height;
final int? durationInSeconds; final int? durationMs;
final String id; final String id;
final String? checksum; final String? checksum;
final bool isFavorite; final bool isFavorite;
@@ -887,7 +884,7 @@ class LocalAssetEntityData extends i0.DataClass
required this.updatedAt, required this.updatedAt,
this.width, this.width,
this.height, this.height,
this.durationInSeconds, this.durationMs,
required this.id, required this.id,
this.checksum, this.checksum,
required this.isFavorite, required this.isFavorite,
@@ -915,8 +912,8 @@ class LocalAssetEntityData extends i0.DataClass
if (!nullToAbsent || height != null) { if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height); map['height'] = i0.Variable<int>(height);
} }
if (!nullToAbsent || durationInSeconds != null) { if (!nullToAbsent || durationMs != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds); map['duration_ms'] = i0.Variable<int>(durationMs);
} }
map['id'] = i0.Variable<String>(id); map['id'] = i0.Variable<String>(id);
if (!nullToAbsent || checksum != null) { if (!nullToAbsent || checksum != null) {
@@ -958,7 +955,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']), width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']), height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), durationMs: serializer.fromJson<int?>(json['durationMs']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
checksum: serializer.fromJson<String?>(json['checksum']), checksum: serializer.fromJson<String?>(json['checksum']),
isFavorite: serializer.fromJson<bool>(json['isFavorite']), isFavorite: serializer.fromJson<bool>(json['isFavorite']),
@@ -984,7 +981,7 @@ class LocalAssetEntityData extends i0.DataClass
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width), 'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height), 'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'durationMs': serializer.toJson<int?>(durationMs),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'checksum': serializer.toJson<String?>(checksum), 'checksum': serializer.toJson<String?>(checksum),
'isFavorite': serializer.toJson<bool>(isFavorite), 'isFavorite': serializer.toJson<bool>(isFavorite),
@@ -1006,7 +1003,7 @@ class LocalAssetEntityData extends i0.DataClass
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
String? id, String? id,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
bool? isFavorite, bool? isFavorite,
@@ -1023,9 +1020,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width, width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height, height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present durationMs: durationMs.present ? durationMs.value : this.durationMs,
? durationInSeconds.value
: this.durationInSeconds,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum.present ? checksum.value : this.checksum, checksum: checksum.present ? checksum.value : this.checksum,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
@@ -1046,9 +1041,9 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width, width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height, height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present durationMs: data.durationMs.present
? data.durationInSeconds.value ? data.durationMs.value
: this.durationInSeconds, : this.durationMs,
id: data.id.present ? data.id.value : this.id, id: data.id.present ? data.id.value : this.id,
checksum: data.checksum.present ? data.checksum.value : this.checksum, checksum: data.checksum.present ? data.checksum.value : this.checksum,
isFavorite: data.isFavorite.present isFavorite: data.isFavorite.present
@@ -1078,7 +1073,7 @@ class LocalAssetEntityData extends i0.DataClass
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ') ..write('isFavorite: $isFavorite, ')
@@ -1100,7 +1095,7 @@ class LocalAssetEntityData extends i0.DataClass
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
checksum, checksum,
isFavorite, isFavorite,
@@ -1121,7 +1116,7 @@ class LocalAssetEntityData extends i0.DataClass
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.width == this.width && other.width == this.width &&
other.height == this.height && other.height == this.height &&
other.durationInSeconds == this.durationInSeconds && other.durationMs == this.durationMs &&
other.id == this.id && other.id == this.id &&
other.checksum == this.checksum && other.checksum == this.checksum &&
other.isFavorite == this.isFavorite && other.isFavorite == this.isFavorite &&
@@ -1141,7 +1136,7 @@ class LocalAssetEntityCompanion
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width; final i0.Value<int?> width;
final i0.Value<int?> height; final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds; final i0.Value<int?> durationMs;
final i0.Value<String> id; final i0.Value<String> id;
final i0.Value<String?> checksum; final i0.Value<String?> checksum;
final i0.Value<bool> isFavorite; final i0.Value<bool> isFavorite;
@@ -1158,7 +1153,7 @@ class LocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(),
@@ -1176,7 +1171,7 @@ class LocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
required String id, required String id,
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(),
@@ -1196,7 +1191,7 @@ class LocalAssetEntityCompanion
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width, i0.Expression<int>? width,
i0.Expression<int>? height, i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds, i0.Expression<int>? durationMs,
i0.Expression<String>? id, i0.Expression<String>? id,
i0.Expression<String>? checksum, i0.Expression<String>? checksum,
i0.Expression<bool>? isFavorite, i0.Expression<bool>? isFavorite,
@@ -1214,7 +1209,7 @@ class LocalAssetEntityCompanion
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width, if (width != null) 'width': width,
if (height != null) 'height': height, if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (durationMs != null) 'duration_ms': durationMs,
if (id != null) 'id': id, if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum, if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite, if (isFavorite != null) 'is_favorite': isFavorite,
@@ -1234,7 +1229,7 @@ class LocalAssetEntityCompanion
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width, i0.Value<int?>? width,
i0.Value<int?>? height, i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds, i0.Value<int?>? durationMs,
i0.Value<String>? id, i0.Value<String>? id,
i0.Value<String?>? checksum, i0.Value<String?>? checksum,
i0.Value<bool>? isFavorite, i0.Value<bool>? isFavorite,
@@ -1252,7 +1247,7 @@ class LocalAssetEntityCompanion
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
@@ -1288,8 +1283,8 @@ class LocalAssetEntityCompanion
if (height.present) { if (height.present) {
map['height'] = i0.Variable<int>(height.value); map['height'] = i0.Variable<int>(height.value);
} }
if (durationInSeconds.present) { if (durationMs.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value); map['duration_ms'] = i0.Variable<int>(durationMs.value);
} }
if (id.present) { if (id.present) {
map['id'] = i0.Variable<String>(id.value); map['id'] = i0.Variable<String>(id.value);
@@ -1334,7 +1329,7 @@ class LocalAssetEntityCompanion
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ') ..write('isFavorite: $isFavorite, ')
@@ -14,7 +14,7 @@ SELECT
rae.updated_at, rae.updated_at,
rae.width, rae.width,
rae.height, rae.height,
rae.duration_in_seconds, rae.duration_ms,
rae.is_favorite, rae.is_favorite,
rae.thumb_hash, rae.thumb_hash,
rae.checksum, rae.checksum,
@@ -52,7 +52,7 @@ SELECT
lae.updated_at, lae.updated_at,
lae.width, lae.width,
lae.height, lae.height,
lae.duration_in_seconds, lae.duration_ms,
lae.is_favorite, lae.is_favorite,
NULL as thumb_hash, NULL as thumb_hash,
lae.checksum, lae.checksum,
+4 -4
View File
@@ -29,7 +29,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
); );
$arrayStartIndex += generatedlimit.amountOfVariables; $arrayStartIndex += generatedlimit.amountOfVariables;
return customSelect( return customSelect(
'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_in_seconds, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_in_seconds, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}', 'SELECT rae.id AS remote_id, (SELECT lae.id FROM local_asset_entity AS lae WHERE lae.checksum = rae.checksum LIMIT 1) AS local_id, rae.name, rae.type, rae.created_at AS created_at, rae.updated_at, rae.width, rae.height, rae.duration_ms, rae.is_favorite, rae.thumb_hash, rae.checksum, rae.owner_id, rae.live_photo_video_id, 0 AS orientation, rae.stack_id, NULL AS i_cloud_id, NULL AS latitude, NULL AS longitude, NULL AS adjustmentTime, rae.is_edited, 0 AS playback_style FROM remote_asset_entity AS rae LEFT JOIN stack_entity AS se ON rae.stack_id = se.id WHERE rae.deleted_at IS NULL AND rae.visibility = 0 AND rae.owner_id IN ($expandeduserIds) AND(rae.stack_id IS NULL OR rae.id = se.primary_asset_id)UNION ALL SELECT NULL AS remote_id, lae.id AS local_id, lae.name, lae.type, lae.created_at AS created_at, lae.updated_at, lae.width, lae.height, lae.duration_ms, lae.is_favorite, NULL AS thumb_hash, lae.checksum, NULL AS owner_id, NULL AS live_photo_video_id, lae.orientation, NULL AS stack_id, lae.i_cloud_id, lae.latitude, lae.longitude, lae.adjustment_time, 0 AS is_edited, lae.playback_style FROM local_asset_entity AS lae WHERE NOT EXISTS (SELECT 1 FROM remote_asset_entity AS rae WHERE rae.checksum = lae.checksum AND rae.owner_id IN ($expandeduserIds)) AND EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 0) AND NOT EXISTS (SELECT 1 FROM local_album_asset_entity AS laa INNER JOIN local_album_entity AS la ON laa.album_id = la.id WHERE laa.asset_id = lae.id AND la.backup_selection = 2) ORDER BY created_at DESC ${generatedlimit.sql}',
variables: [ variables: [
for (var $ in userIds) i0.Variable<String>($), for (var $ in userIds) i0.Variable<String>($),
...generatedlimit.introducedVariables, ...generatedlimit.introducedVariables,
@@ -54,7 +54,7 @@ class MergedAssetDrift extends i1.ModularAccessor {
updatedAt: row.read<DateTime>('updated_at'), updatedAt: row.read<DateTime>('updated_at'),
width: row.readNullable<int>('width'), width: row.readNullable<int>('width'),
height: row.readNullable<int>('height'), height: row.readNullable<int>('height'),
durationInSeconds: row.readNullable<int>('duration_in_seconds'), durationMs: row.readNullable<int>('duration_ms'),
isFavorite: row.read<bool>('is_favorite'), isFavorite: row.read<bool>('is_favorite'),
thumbHash: row.readNullable<String>('thumb_hash'), thumbHash: row.readNullable<String>('thumb_hash'),
checksum: row.readNullable<String>('checksum'), checksum: row.readNullable<String>('checksum'),
@@ -127,7 +127,7 @@ class MergedAssetResult {
final DateTime updatedAt; final DateTime updatedAt;
final int? width; final int? width;
final int? height; final int? height;
final int? durationInSeconds; final int? durationMs;
final bool isFavorite; final bool isFavorite;
final String? thumbHash; final String? thumbHash;
final String? checksum; final String? checksum;
@@ -150,7 +150,7 @@ class MergedAssetResult {
required this.updatedAt, required this.updatedAt,
this.width, this.width,
this.height, this.height,
this.durationInSeconds, this.durationMs,
required this.isFavorite, required this.isFavorite,
this.thumbHash, this.thumbHash,
this.checksum, this.checksum,
@@ -1,10 +1,8 @@
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:immich_mobile/domain/models/album/album.model.dart'; import 'package:immich_mobile/domain/models/album/album.model.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)')
class RemoteAlbumEntity extends Table with DriftDefaultsMixin { class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
const RemoteAlbumEntity(); const RemoteAlbumEntity();
@@ -18,8 +16,6 @@ class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
TextColumn get ownerId => text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
TextColumn get thumbnailAssetId => TextColumn get thumbnailAssetId =>
text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()(); text().references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull).nullable()();
@@ -7,11 +7,9 @@ import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart' import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
as i3; as i3;
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4; import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart' import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i5; as i5;
import 'package:drift/internal/modular.dart' as i6; import 'package:drift/internal/modular.dart' as i6;
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
as i7;
typedef $$RemoteAlbumEntityTableCreateCompanionBuilder = typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
i1.RemoteAlbumEntityCompanion Function({ i1.RemoteAlbumEntityCompanion Function({
@@ -20,7 +18,6 @@ typedef $$RemoteAlbumEntityTableCreateCompanionBuilder =
i0.Value<String> description, i0.Value<String> description,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
required String ownerId,
i0.Value<String?> thumbnailAssetId, i0.Value<String?> thumbnailAssetId,
i0.Value<bool> isActivityEnabled, i0.Value<bool> isActivityEnabled,
required i2.AlbumAssetOrder order, required i2.AlbumAssetOrder order,
@@ -32,7 +29,6 @@ typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder =
i0.Value<String> description, i0.Value<String> description,
i0.Value<DateTime> createdAt, i0.Value<DateTime> createdAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<String> ownerId,
i0.Value<String?> thumbnailAssetId, i0.Value<String?> thumbnailAssetId,
i0.Value<bool> isActivityEnabled, i0.Value<bool> isActivityEnabled,
i0.Value<i2.AlbumAssetOrder> order, i0.Value<i2.AlbumAssetOrder> order,
@@ -51,42 +47,10 @@ final class $$RemoteAlbumEntityTableReferences
super.$_typedResult, super.$_typedResult,
); );
static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) => static i5.$RemoteAssetEntityTable _thumbnailAssetIdTable(
i6.ReadDatabaseContainer(db)
.resultSet<i5.$UserEntityTable>('user_entity')
.createAlias(
i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db)
.resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
.ownerId,
i6.ReadDatabaseContainer(
db,
).resultSet<i5.$UserEntityTable>('user_entity').id,
),
);
i5.$$UserEntityTableProcessedTableManager get ownerId {
final $_column = $_itemColumn<String>('owner_id')!;
final manager = i5
.$$UserEntityTableTableManager(
$_db,
i6.ReadDatabaseContainer(
$_db,
).resultSet<i5.$UserEntityTable>('user_entity'),
)
.filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
if (item == null) return manager;
return i0.ProcessedTableManager(
manager.$state.copyWith(prefetchedData: [item]),
);
}
static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
i0.GeneratedDatabase db, i0.GeneratedDatabase db,
) => i6.ReadDatabaseContainer(db) ) => i6.ReadDatabaseContainer(db)
.resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity') .resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity')
.createAlias( .createAlias(
i0.$_aliasNameGenerator( i0.$_aliasNameGenerator(
i6.ReadDatabaseContainer(db) i6.ReadDatabaseContainer(db)
@@ -94,19 +58,19 @@ final class $$RemoteAlbumEntityTableReferences
.thumbnailAssetId, .thumbnailAssetId,
i6.ReadDatabaseContainer( i6.ReadDatabaseContainer(
db, db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity').id, ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity').id,
), ),
); );
i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId { i5.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
final $_column = $_itemColumn<String>('thumbnail_asset_id'); final $_column = $_itemColumn<String>('thumbnail_asset_id');
if ($_column == null) return null; if ($_column == null) return null;
final manager = i7 final manager = i5
.$$RemoteAssetEntityTableTableManager( .$$RemoteAssetEntityTableTableManager(
$_db, $_db,
i6.ReadDatabaseContainer( i6.ReadDatabaseContainer(
$_db, $_db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
) )
.filter((f) => f.id.sqlEquals($_column)); .filter((f) => f.id.sqlEquals($_column));
final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db)); final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
@@ -162,51 +126,24 @@ class $$RemoteAlbumEntityTableFilterComposer
builder: (column) => i0.ColumnWithTypeConverterFilters(column), builder: (column) => i0.ColumnWithTypeConverterFilters(column),
); );
i5.$$UserEntityTableFilterComposer get ownerId { i5.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
final i5.$$UserEntityTableFilterComposer composer = $composerBuilder( final i5.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableFilterComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId, getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer( referencedTable: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id, getReferencedColumn: (t) => t.id,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => i7.$$RemoteAssetEntityTableFilterComposer( }) => i5.$$RemoteAssetEntityTableFilterComposer(
$db: $db, $db: $db,
$table: i6.ReadDatabaseContainer( $table: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -261,52 +198,25 @@ class $$RemoteAlbumEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i5.$$UserEntityTableOrderingComposer get ownerId { i5.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder( final i5.$$RemoteAssetEntityTableOrderingComposer composer =
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableOrderingComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableOrderingComposer composer =
$composerBuilder( $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId, getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer( referencedTable: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id, getReferencedColumn: (t) => t.id,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => i7.$$RemoteAssetEntityTableOrderingComposer( }) => i5.$$RemoteAssetEntityTableOrderingComposer(
$db: $db, $db: $db,
$table: i6.ReadDatabaseContainer( $table: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -351,52 +261,25 @@ class $$RemoteAlbumEntityTableAnnotationComposer
i0.GeneratedColumnWithTypeConverter<i2.AlbumAssetOrder, int> get order => i0.GeneratedColumnWithTypeConverter<i2.AlbumAssetOrder, int> get order =>
$composableBuilder(column: $table.order, builder: (column) => column); $composableBuilder(column: $table.order, builder: (column) => column);
i5.$$UserEntityTableAnnotationComposer get ownerId { i5.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder( final i5.$$RemoteAssetEntityTableAnnotationComposer composer =
composer: this,
getCurrentColumn: (t) => t.ownerId,
referencedTable: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
getReferencedColumn: (t) => t.id,
builder:
(
joinBuilder, {
$addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer,
}) => i5.$$UserEntityTableAnnotationComposer(
$db: $db,
$table: i6.ReadDatabaseContainer(
$db,
).resultSet<i5.$UserEntityTable>('user_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer:
$removeJoinBuilderFromRootComposer,
),
);
return composer;
}
i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
$composerBuilder( $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.thumbnailAssetId, getCurrentColumn: (t) => t.thumbnailAssetId,
referencedTable: i6.ReadDatabaseContainer( referencedTable: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
getReferencedColumn: (t) => t.id, getReferencedColumn: (t) => t.id,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => i7.$$RemoteAssetEntityTableAnnotationComposer( }) => i5.$$RemoteAssetEntityTableAnnotationComposer(
$db: $db, $db: $db,
$table: i6.ReadDatabaseContainer( $table: i6.ReadDatabaseContainer(
$db, $db,
).resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'), ).resultSet<i5.$RemoteAssetEntityTable>('remote_asset_entity'),
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -420,7 +303,7 @@ class $$RemoteAlbumEntityTableTableManager
$$RemoteAlbumEntityTableUpdateCompanionBuilder, $$RemoteAlbumEntityTableUpdateCompanionBuilder,
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
i1.RemoteAlbumEntityData, i1.RemoteAlbumEntityData,
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) i0.PrefetchHooks Function({bool thumbnailAssetId})
> { > {
$$RemoteAlbumEntityTableTableManager( $$RemoteAlbumEntityTableTableManager(
i0.GeneratedDatabase db, i0.GeneratedDatabase db,
@@ -445,7 +328,6 @@ class $$RemoteAlbumEntityTableTableManager
i0.Value<String> description = const i0.Value.absent(), i0.Value<String> description = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<String> ownerId = const i0.Value.absent(),
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(), i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
i0.Value<bool> isActivityEnabled = const i0.Value.absent(), i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
i0.Value<i2.AlbumAssetOrder> order = const i0.Value.absent(), i0.Value<i2.AlbumAssetOrder> order = const i0.Value.absent(),
@@ -455,7 +337,6 @@ class $$RemoteAlbumEntityTableTableManager
description: description, description: description,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
ownerId: ownerId,
thumbnailAssetId: thumbnailAssetId, thumbnailAssetId: thumbnailAssetId,
isActivityEnabled: isActivityEnabled, isActivityEnabled: isActivityEnabled,
order: order, order: order,
@@ -467,7 +348,6 @@ class $$RemoteAlbumEntityTableTableManager
i0.Value<String> description = const i0.Value.absent(), i0.Value<String> description = const i0.Value.absent(),
i0.Value<DateTime> createdAt = const i0.Value.absent(), i0.Value<DateTime> createdAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
required String ownerId,
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(), i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
i0.Value<bool> isActivityEnabled = const i0.Value.absent(), i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
required i2.AlbumAssetOrder order, required i2.AlbumAssetOrder order,
@@ -477,7 +357,6 @@ class $$RemoteAlbumEntityTableTableManager
description: description, description: description,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
ownerId: ownerId,
thumbnailAssetId: thumbnailAssetId, thumbnailAssetId: thumbnailAssetId,
isActivityEnabled: isActivityEnabled, isActivityEnabled: isActivityEnabled,
order: order, order: order,
@@ -490,7 +369,7 @@ class $$RemoteAlbumEntityTableTableManager
), ),
) )
.toList(), .toList(),
prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) { prefetchHooksCallback: ({thumbnailAssetId = false}) {
return i0.PrefetchHooks( return i0.PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [], explicitlyWatchedTables: [],
@@ -510,21 +389,6 @@ class $$RemoteAlbumEntityTableTableManager
dynamic dynamic
> >
>(state) { >(state) {
if (ownerId) {
state =
state.withJoin(
currentTable: table,
currentColumn: table.ownerId,
referencedTable: i1
.$$RemoteAlbumEntityTableReferences
._ownerIdTable(db),
referencedColumn: i1
.$$RemoteAlbumEntityTableReferences
._ownerIdTable(db)
.id,
)
as T;
}
if (thumbnailAssetId) { if (thumbnailAssetId) {
state = state =
state.withJoin( state.withJoin(
@@ -564,12 +428,8 @@ typedef $$RemoteAlbumEntityTableProcessedTableManager =
$$RemoteAlbumEntityTableUpdateCompanionBuilder, $$RemoteAlbumEntityTableUpdateCompanionBuilder,
(i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences), (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
i1.RemoteAlbumEntityData, i1.RemoteAlbumEntityData,
i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId}) i0.PrefetchHooks Function({bool thumbnailAssetId})
>; >;
i0.Index get idxRemoteAlbumOwnerId => i0.Index(
'idx_remote_album_owner_id',
'CREATE INDEX IF NOT EXISTS idx_remote_album_owner_id ON remote_album_entity (owner_id)',
);
class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> { with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
@@ -636,20 +496,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: i4.currentDateAndTime, defaultValue: i4.currentDateAndTime,
); );
static const i0.VerificationMeta _ownerIdMeta = const i0.VerificationMeta(
'ownerId',
);
@override
late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
'owner_id',
aliasedName,
false,
type: i0.DriftSqlType.string,
requiredDuringInsert: true,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'REFERENCES user_entity (id) ON DELETE CASCADE',
),
);
static const i0.VerificationMeta _thumbnailAssetIdMeta = static const i0.VerificationMeta _thumbnailAssetIdMeta =
const i0.VerificationMeta('thumbnailAssetId'); const i0.VerificationMeta('thumbnailAssetId');
@override @override
@@ -698,7 +544,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
description, description,
createdAt, createdAt,
updatedAt, updatedAt,
ownerId,
thumbnailAssetId, thumbnailAssetId,
isActivityEnabled, isActivityEnabled,
order, order,
@@ -749,14 +594,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
); );
} }
if (data.containsKey('owner_id')) {
context.handle(
_ownerIdMeta,
ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta),
);
} else if (isInserting) {
context.missing(_ownerIdMeta);
}
if (data.containsKey('thumbnail_asset_id')) { if (data.containsKey('thumbnail_asset_id')) {
context.handle( context.handle(
_thumbnailAssetIdMeta, _thumbnailAssetIdMeta,
@@ -807,10 +644,6 @@ class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
i0.DriftSqlType.dateTime, i0.DriftSqlType.dateTime,
data['${effectivePrefix}updated_at'], data['${effectivePrefix}updated_at'],
)!, )!,
ownerId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string,
data['${effectivePrefix}owner_id'],
)!,
thumbnailAssetId: attachedDatabase.typeMapping.read( thumbnailAssetId: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, i0.DriftSqlType.string,
data['${effectivePrefix}thumbnail_asset_id'], data['${effectivePrefix}thumbnail_asset_id'],
@@ -850,7 +683,6 @@ class RemoteAlbumEntityData extends i0.DataClass
final String description; final String description;
final DateTime createdAt; final DateTime createdAt;
final DateTime updatedAt; final DateTime updatedAt;
final String ownerId;
final String? thumbnailAssetId; final String? thumbnailAssetId;
final bool isActivityEnabled; final bool isActivityEnabled;
final i2.AlbumAssetOrder order; final i2.AlbumAssetOrder order;
@@ -860,7 +692,6 @@ class RemoteAlbumEntityData extends i0.DataClass
required this.description, required this.description,
required this.createdAt, required this.createdAt,
required this.updatedAt, required this.updatedAt,
required this.ownerId,
this.thumbnailAssetId, this.thumbnailAssetId,
required this.isActivityEnabled, required this.isActivityEnabled,
required this.order, required this.order,
@@ -873,7 +704,6 @@ class RemoteAlbumEntityData extends i0.DataClass
map['description'] = i0.Variable<String>(description); map['description'] = i0.Variable<String>(description);
map['created_at'] = i0.Variable<DateTime>(createdAt); map['created_at'] = i0.Variable<DateTime>(createdAt);
map['updated_at'] = i0.Variable<DateTime>(updatedAt); map['updated_at'] = i0.Variable<DateTime>(updatedAt);
map['owner_id'] = i0.Variable<String>(ownerId);
if (!nullToAbsent || thumbnailAssetId != null) { if (!nullToAbsent || thumbnailAssetId != null) {
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId); map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
} }
@@ -897,7 +727,6 @@ class RemoteAlbumEntityData extends i0.DataClass
description: serializer.fromJson<String>(json['description']), description: serializer.fromJson<String>(json['description']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
ownerId: serializer.fromJson<String>(json['ownerId']),
thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']), thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']), isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson( order: i1.$RemoteAlbumEntityTable.$converterorder.fromJson(
@@ -914,7 +743,6 @@ class RemoteAlbumEntityData extends i0.DataClass
'description': serializer.toJson<String>(description), 'description': serializer.toJson<String>(description),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'ownerId': serializer.toJson<String>(ownerId),
'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId), 'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled), 'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
'order': serializer.toJson<int>( 'order': serializer.toJson<int>(
@@ -929,7 +757,6 @@ class RemoteAlbumEntityData extends i0.DataClass
String? description, String? description,
DateTime? createdAt, DateTime? createdAt,
DateTime? updatedAt, DateTime? updatedAt,
String? ownerId,
i0.Value<String?> thumbnailAssetId = const i0.Value.absent(), i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
bool? isActivityEnabled, bool? isActivityEnabled,
i2.AlbumAssetOrder? order, i2.AlbumAssetOrder? order,
@@ -939,7 +766,6 @@ class RemoteAlbumEntityData extends i0.DataClass
description: description ?? this.description, description: description ?? this.description,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
thumbnailAssetId: thumbnailAssetId.present thumbnailAssetId: thumbnailAssetId.present
? thumbnailAssetId.value ? thumbnailAssetId.value
: this.thumbnailAssetId, : this.thumbnailAssetId,
@@ -955,7 +781,6 @@ class RemoteAlbumEntityData extends i0.DataClass
: this.description, : this.description,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
thumbnailAssetId: data.thumbnailAssetId.present thumbnailAssetId: data.thumbnailAssetId.present
? data.thumbnailAssetId.value ? data.thumbnailAssetId.value
: this.thumbnailAssetId, : this.thumbnailAssetId,
@@ -974,7 +799,6 @@ class RemoteAlbumEntityData extends i0.DataClass
..write('description: $description, ') ..write('description: $description, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('ownerId: $ownerId, ')
..write('thumbnailAssetId: $thumbnailAssetId, ') ..write('thumbnailAssetId: $thumbnailAssetId, ')
..write('isActivityEnabled: $isActivityEnabled, ') ..write('isActivityEnabled: $isActivityEnabled, ')
..write('order: $order') ..write('order: $order')
@@ -989,7 +813,6 @@ class RemoteAlbumEntityData extends i0.DataClass
description, description,
createdAt, createdAt,
updatedAt, updatedAt,
ownerId,
thumbnailAssetId, thumbnailAssetId,
isActivityEnabled, isActivityEnabled,
order, order,
@@ -1003,7 +826,6 @@ class RemoteAlbumEntityData extends i0.DataClass
other.description == this.description && other.description == this.description &&
other.createdAt == this.createdAt && other.createdAt == this.createdAt &&
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.ownerId == this.ownerId &&
other.thumbnailAssetId == this.thumbnailAssetId && other.thumbnailAssetId == this.thumbnailAssetId &&
other.isActivityEnabled == this.isActivityEnabled && other.isActivityEnabled == this.isActivityEnabled &&
other.order == this.order); other.order == this.order);
@@ -1016,7 +838,6 @@ class RemoteAlbumEntityCompanion
final i0.Value<String> description; final i0.Value<String> description;
final i0.Value<DateTime> createdAt; final i0.Value<DateTime> createdAt;
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<String> ownerId;
final i0.Value<String?> thumbnailAssetId; final i0.Value<String?> thumbnailAssetId;
final i0.Value<bool> isActivityEnabled; final i0.Value<bool> isActivityEnabled;
final i0.Value<i2.AlbumAssetOrder> order; final i0.Value<i2.AlbumAssetOrder> order;
@@ -1026,7 +847,6 @@ class RemoteAlbumEntityCompanion
this.description = const i0.Value.absent(), this.description = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.ownerId = const i0.Value.absent(),
this.thumbnailAssetId = const i0.Value.absent(), this.thumbnailAssetId = const i0.Value.absent(),
this.isActivityEnabled = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(),
this.order = const i0.Value.absent(), this.order = const i0.Value.absent(),
@@ -1037,13 +857,11 @@ class RemoteAlbumEntityCompanion
this.description = const i0.Value.absent(), this.description = const i0.Value.absent(),
this.createdAt = const i0.Value.absent(), this.createdAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
required String ownerId,
this.thumbnailAssetId = const i0.Value.absent(), this.thumbnailAssetId = const i0.Value.absent(),
this.isActivityEnabled = const i0.Value.absent(), this.isActivityEnabled = const i0.Value.absent(),
required i2.AlbumAssetOrder order, required i2.AlbumAssetOrder order,
}) : id = i0.Value(id), }) : id = i0.Value(id),
name = i0.Value(name), name = i0.Value(name),
ownerId = i0.Value(ownerId),
order = i0.Value(order); order = i0.Value(order);
static i0.Insertable<i1.RemoteAlbumEntityData> custom({ static i0.Insertable<i1.RemoteAlbumEntityData> custom({
i0.Expression<String>? id, i0.Expression<String>? id,
@@ -1051,7 +869,6 @@ class RemoteAlbumEntityCompanion
i0.Expression<String>? description, i0.Expression<String>? description,
i0.Expression<DateTime>? createdAt, i0.Expression<DateTime>? createdAt,
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<String>? ownerId,
i0.Expression<String>? thumbnailAssetId, i0.Expression<String>? thumbnailAssetId,
i0.Expression<bool>? isActivityEnabled, i0.Expression<bool>? isActivityEnabled,
i0.Expression<int>? order, i0.Expression<int>? order,
@@ -1062,7 +879,6 @@ class RemoteAlbumEntityCompanion
if (description != null) 'description': description, if (description != null) 'description': description,
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (ownerId != null) 'owner_id': ownerId,
if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId, if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId,
if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled, if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled,
if (order != null) 'order': order, if (order != null) 'order': order,
@@ -1075,7 +891,6 @@ class RemoteAlbumEntityCompanion
i0.Value<String>? description, i0.Value<String>? description,
i0.Value<DateTime>? createdAt, i0.Value<DateTime>? createdAt,
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<String>? ownerId,
i0.Value<String?>? thumbnailAssetId, i0.Value<String?>? thumbnailAssetId,
i0.Value<bool>? isActivityEnabled, i0.Value<bool>? isActivityEnabled,
i0.Value<i2.AlbumAssetOrder>? order, i0.Value<i2.AlbumAssetOrder>? order,
@@ -1086,7 +901,6 @@ class RemoteAlbumEntityCompanion
description: description ?? this.description, description: description ?? this.description,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
ownerId: ownerId ?? this.ownerId,
thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId, thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled, isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
order: order ?? this.order, order: order ?? this.order,
@@ -1111,9 +925,6 @@ class RemoteAlbumEntityCompanion
if (updatedAt.present) { if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value); map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
} }
if (ownerId.present) {
map['owner_id'] = i0.Variable<String>(ownerId.value);
}
if (thumbnailAssetId.present) { if (thumbnailAssetId.present) {
map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value); map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
} }
@@ -1136,7 +947,6 @@ class RemoteAlbumEntityCompanion
..write('description: $description, ') ..write('description: $description, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('ownerId: $ownerId, ')
..write('thumbnailAssetId: $thumbnailAssetId, ') ..write('thumbnailAssetId: $thumbnailAssetId, ')
..write('isActivityEnabled: $isActivityEnabled, ') ..write('isActivityEnabled: $isActivityEnabled, ')
..write('order: $order') ..write('order: $order')
@@ -66,7 +66,7 @@ extension RemoteAssetEntityDataDomainEx on RemoteAssetEntityData {
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
durationInSeconds: durationInSeconds, durationMs: durationMs,
isFavorite: isFavorite, isFavorite: isFavorite,
height: height, height: height,
width: width, width: width,
@@ -19,7 +19,7 @@ typedef $$RemoteAssetEntityTableCreateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
required String id, required String id,
required String checksum, required String checksum,
i0.Value<bool> isFavorite, i0.Value<bool> isFavorite,
@@ -41,7 +41,7 @@ typedef $$RemoteAssetEntityTableUpdateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
i0.Value<String> id, i0.Value<String> id,
i0.Value<String> checksum, i0.Value<String> checksum,
i0.Value<bool> isFavorite, i0.Value<bool> isFavorite,
@@ -142,8 +142,8 @@ class $$RemoteAssetEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder( i0.ColumnFilters<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
@@ -270,8 +270,8 @@ class $$RemoteAssetEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder( i0.ColumnOrderings<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
@@ -385,8 +385,8 @@ class $$RemoteAssetEntityTableAnnotationComposer
i0.GeneratedColumn<int> get height => i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column); $composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder( i0.GeneratedColumn<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => column, builder: (column) => column,
); );
@@ -499,7 +499,7 @@ class $$RemoteAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(), i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> checksum = const i0.Value.absent(), i0.Value<String> checksum = const i0.Value.absent(),
i0.Value<bool> isFavorite = const i0.Value.absent(), i0.Value<bool> isFavorite = const i0.Value.absent(),
@@ -520,7 +520,7 @@ class $$RemoteAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
checksum: checksum, checksum: checksum,
isFavorite: isFavorite, isFavorite: isFavorite,
@@ -542,7 +542,7 @@ class $$RemoteAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
required String id, required String id,
required String checksum, required String checksum,
i0.Value<bool> isFavorite = const i0.Value.absent(), i0.Value<bool> isFavorite = const i0.Value.absent(),
@@ -562,7 +562,7 @@ class $$RemoteAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
checksum: checksum, checksum: checksum,
isFavorite: isFavorite, isFavorite: isFavorite,
@@ -724,17 +724,17 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
type: i0.DriftSqlType.int, type: i0.DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
); );
static const i0.VerificationMeta _durationInSecondsMeta = static const i0.VerificationMeta _durationMsMeta = const i0.VerificationMeta(
const i0.VerificationMeta('durationInSeconds'); 'durationMs',
);
@override @override
late final i0.GeneratedColumn<int> durationInSeconds = late final i0.GeneratedColumn<int> durationMs = i0.GeneratedColumn<int>(
i0.GeneratedColumn<int>( 'duration_ms',
'duration_in_seconds', aliasedName,
aliasedName, true,
true, type: i0.DriftSqlType.int,
type: i0.DriftSqlType.int, requiredDuringInsert: false,
requiredDuringInsert: false, );
);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override @override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>( late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
@@ -886,7 +886,7 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
checksum, checksum,
isFavorite, isFavorite,
@@ -944,13 +944,10 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
height.isAcceptableOrUnknown(data['height']!, _heightMeta), height.isAcceptableOrUnknown(data['height']!, _heightMeta),
); );
} }
if (data.containsKey('duration_in_seconds')) { if (data.containsKey('duration_ms')) {
context.handle( context.handle(
_durationInSecondsMeta, _durationMsMeta,
durationInSeconds.isAcceptableOrUnknown( durationMs.isAcceptableOrUnknown(data['duration_ms']!, _durationMsMeta),
data['duration_in_seconds']!,
_durationInSecondsMeta,
),
); );
} }
if (data.containsKey('id')) { if (data.containsKey('id')) {
@@ -1066,9 +1063,9 @@ class $RemoteAssetEntityTable extends i3.RemoteAssetEntity
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}height'], data['${effectivePrefix}height'],
), ),
durationInSeconds: attachedDatabase.typeMapping.read( durationMs: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}duration_in_seconds'], data['${effectivePrefix}duration_ms'],
), ),
id: attachedDatabase.typeMapping.read( id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, i0.DriftSqlType.string,
@@ -1148,7 +1145,7 @@ class RemoteAssetEntityData extends i0.DataClass
final DateTime updatedAt; final DateTime updatedAt;
final int? width; final int? width;
final int? height; final int? height;
final int? durationInSeconds; final int? durationMs;
final String id; final String id;
final String checksum; final String checksum;
final bool isFavorite; final bool isFavorite;
@@ -1168,7 +1165,7 @@ class RemoteAssetEntityData extends i0.DataClass
required this.updatedAt, required this.updatedAt,
this.width, this.width,
this.height, this.height,
this.durationInSeconds, this.durationMs,
required this.id, required this.id,
required this.checksum, required this.checksum,
required this.isFavorite, required this.isFavorite,
@@ -1199,8 +1196,8 @@ class RemoteAssetEntityData extends i0.DataClass
if (!nullToAbsent || height != null) { if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height); map['height'] = i0.Variable<int>(height);
} }
if (!nullToAbsent || durationInSeconds != null) { if (!nullToAbsent || durationMs != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds); map['duration_ms'] = i0.Variable<int>(durationMs);
} }
map['id'] = i0.Variable<String>(id); map['id'] = i0.Variable<String>(id);
map['checksum'] = i0.Variable<String>(checksum); map['checksum'] = i0.Variable<String>(checksum);
@@ -1247,7 +1244,7 @@ class RemoteAssetEntityData extends i0.DataClass
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']), width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']), height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), durationMs: serializer.fromJson<int?>(json['durationMs']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
checksum: serializer.fromJson<String>(json['checksum']), checksum: serializer.fromJson<String>(json['checksum']),
isFavorite: serializer.fromJson<bool>(json['isFavorite']), isFavorite: serializer.fromJson<bool>(json['isFavorite']),
@@ -1276,7 +1273,7 @@ class RemoteAssetEntityData extends i0.DataClass
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width), 'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height), 'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'durationMs': serializer.toJson<int?>(durationMs),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'checksum': serializer.toJson<String>(checksum), 'checksum': serializer.toJson<String>(checksum),
'isFavorite': serializer.toJson<bool>(isFavorite), 'isFavorite': serializer.toJson<bool>(isFavorite),
@@ -1301,7 +1298,7 @@ class RemoteAssetEntityData extends i0.DataClass
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
String? id, String? id,
String? checksum, String? checksum,
bool? isFavorite, bool? isFavorite,
@@ -1321,9 +1318,7 @@ class RemoteAssetEntityData extends i0.DataClass
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width, width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height, height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present durationMs: durationMs.present ? durationMs.value : this.durationMs,
? durationInSeconds.value
: this.durationInSeconds,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
@@ -1349,9 +1344,9 @@ class RemoteAssetEntityData extends i0.DataClass
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width, width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height, height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present durationMs: data.durationMs.present
? data.durationInSeconds.value ? data.durationMs.value
: this.durationInSeconds, : this.durationMs,
id: data.id.present ? data.id.value : this.id, id: data.id.present ? data.id.value : this.id,
checksum: data.checksum.present ? data.checksum.value : this.checksum, checksum: data.checksum.present ? data.checksum.value : this.checksum,
isFavorite: data.isFavorite.present isFavorite: data.isFavorite.present
@@ -1384,7 +1379,7 @@ class RemoteAssetEntityData extends i0.DataClass
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ') ..write('isFavorite: $isFavorite, ')
@@ -1409,7 +1404,7 @@ class RemoteAssetEntityData extends i0.DataClass
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
checksum, checksum,
isFavorite, isFavorite,
@@ -1433,7 +1428,7 @@ class RemoteAssetEntityData extends i0.DataClass
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.width == this.width && other.width == this.width &&
other.height == this.height && other.height == this.height &&
other.durationInSeconds == this.durationInSeconds && other.durationMs == this.durationMs &&
other.id == this.id && other.id == this.id &&
other.checksum == this.checksum && other.checksum == this.checksum &&
other.isFavorite == this.isFavorite && other.isFavorite == this.isFavorite &&
@@ -1456,7 +1451,7 @@ class RemoteAssetEntityCompanion
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width; final i0.Value<int?> width;
final i0.Value<int?> height; final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds; final i0.Value<int?> durationMs;
final i0.Value<String> id; final i0.Value<String> id;
final i0.Value<String> checksum; final i0.Value<String> checksum;
final i0.Value<bool> isFavorite; final i0.Value<bool> isFavorite;
@@ -1476,7 +1471,7 @@ class RemoteAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
this.isFavorite = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(),
@@ -1497,7 +1492,7 @@ class RemoteAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
required String id, required String id,
required String checksum, required String checksum,
this.isFavorite = const i0.Value.absent(), this.isFavorite = const i0.Value.absent(),
@@ -1523,7 +1518,7 @@ class RemoteAssetEntityCompanion
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width, i0.Expression<int>? width,
i0.Expression<int>? height, i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds, i0.Expression<int>? durationMs,
i0.Expression<String>? id, i0.Expression<String>? id,
i0.Expression<String>? checksum, i0.Expression<String>? checksum,
i0.Expression<bool>? isFavorite, i0.Expression<bool>? isFavorite,
@@ -1544,7 +1539,7 @@ class RemoteAssetEntityCompanion
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width, if (width != null) 'width': width,
if (height != null) 'height': height, if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (durationMs != null) 'duration_ms': durationMs,
if (id != null) 'id': id, if (id != null) 'id': id,
if (checksum != null) 'checksum': checksum, if (checksum != null) 'checksum': checksum,
if (isFavorite != null) 'is_favorite': isFavorite, if (isFavorite != null) 'is_favorite': isFavorite,
@@ -1567,7 +1562,7 @@ class RemoteAssetEntityCompanion
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width, i0.Value<int?>? width,
i0.Value<int?>? height, i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds, i0.Value<int?>? durationMs,
i0.Value<String>? id, i0.Value<String>? id,
i0.Value<String>? checksum, i0.Value<String>? checksum,
i0.Value<bool>? isFavorite, i0.Value<bool>? isFavorite,
@@ -1588,7 +1583,7 @@ class RemoteAssetEntityCompanion
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
id: id ?? this.id, id: id ?? this.id,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
isFavorite: isFavorite ?? this.isFavorite, isFavorite: isFavorite ?? this.isFavorite,
@@ -1627,8 +1622,8 @@ class RemoteAssetEntityCompanion
if (height.present) { if (height.present) {
map['height'] = i0.Variable<int>(height.value); map['height'] = i0.Variable<int>(height.value);
} }
if (durationInSeconds.present) { if (durationMs.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value); map['duration_ms'] = i0.Variable<int>(durationMs.value);
} }
if (id.present) { if (id.present) {
map['id'] = i0.Variable<String>(id.value); map['id'] = i0.Variable<String>(id.value);
@@ -1680,7 +1675,7 @@ class RemoteAssetEntityCompanion
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
..write('isFavorite: $isFavorite, ') ..write('isFavorite: $isFavorite, ')
@@ -42,7 +42,7 @@ extension TrashedLocalAssetEntityDataDomainExtension on TrashedLocalAssetEntityD
type: type, type: type,
createdAt: createdAt, createdAt: createdAt,
updatedAt: updatedAt, updatedAt: updatedAt,
durationInSeconds: durationInSeconds, durationMs: durationMs,
isFavorite: isFavorite, isFavorite: isFavorite,
height: height, height: height,
width: width, width: width,
@@ -16,7 +16,7 @@ typedef $$TrashedLocalAssetEntityTableCreateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
required String id, required String id,
required String albumId, required String albumId,
i0.Value<String?> checksum, i0.Value<String?> checksum,
@@ -33,7 +33,7 @@ typedef $$TrashedLocalAssetEntityTableUpdateCompanionBuilder =
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> width, i0.Value<int?> width,
i0.Value<int?> height, i0.Value<int?> height,
i0.Value<int?> durationInSeconds, i0.Value<int?> durationMs,
i0.Value<String> id, i0.Value<String> id,
i0.Value<String> albumId, i0.Value<String> albumId,
i0.Value<String?> checksum, i0.Value<String?> checksum,
@@ -84,8 +84,8 @@ class $$TrashedLocalAssetEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<int> get durationInSeconds => $composableBuilder( i0.ColumnFilters<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
@@ -171,8 +171,8 @@ class $$TrashedLocalAssetEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<int> get durationInSeconds => $composableBuilder( i0.ColumnOrderings<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
@@ -240,8 +240,8 @@ class $$TrashedLocalAssetEntityTableAnnotationComposer
i0.GeneratedColumn<int> get height => i0.GeneratedColumn<int> get height =>
$composableBuilder(column: $table.height, builder: (column) => column); $composableBuilder(column: $table.height, builder: (column) => column);
i0.GeneratedColumn<int> get durationInSeconds => $composableBuilder( i0.GeneratedColumn<int> get durationMs => $composableBuilder(
column: $table.durationInSeconds, column: $table.durationMs,
builder: (column) => column, builder: (column) => column,
); );
@@ -326,7 +326,7 @@ class $$TrashedLocalAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
i0.Value<String> id = const i0.Value.absent(), i0.Value<String> id = const i0.Value.absent(),
i0.Value<String> albumId = const i0.Value.absent(), i0.Value<String> albumId = const i0.Value.absent(),
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@@ -342,7 +342,7 @@ class $$TrashedLocalAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
albumId: albumId, albumId: albumId,
checksum: checksum, checksum: checksum,
@@ -359,7 +359,7 @@ class $$TrashedLocalAssetEntityTableTableManager
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
required String id, required String id,
required String albumId, required String albumId,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@@ -375,7 +375,7 @@ class $$TrashedLocalAssetEntityTableTableManager
updatedAt: updatedAt, updatedAt: updatedAt,
width: width, width: width,
height: height, height: height,
durationInSeconds: durationInSeconds, durationMs: durationMs,
id: id, id: id,
albumId: albumId, albumId: albumId,
checksum: checksum, checksum: checksum,
@@ -498,17 +498,17 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
type: i0.DriftSqlType.int, type: i0.DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
); );
static const i0.VerificationMeta _durationInSecondsMeta = static const i0.VerificationMeta _durationMsMeta = const i0.VerificationMeta(
const i0.VerificationMeta('durationInSeconds'); 'durationMs',
);
@override @override
late final i0.GeneratedColumn<int> durationInSeconds = late final i0.GeneratedColumn<int> durationMs = i0.GeneratedColumn<int>(
i0.GeneratedColumn<int>( 'duration_ms',
'duration_in_seconds', aliasedName,
aliasedName, true,
true, type: i0.DriftSqlType.int,
type: i0.DriftSqlType.int, requiredDuringInsert: false,
requiredDuringInsert: false, );
);
static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id'); static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
@override @override
late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>( late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
@@ -599,7 +599,7 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
albumId, albumId,
checksum, checksum,
@@ -652,13 +652,10 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
height.isAcceptableOrUnknown(data['height']!, _heightMeta), height.isAcceptableOrUnknown(data['height']!, _heightMeta),
); );
} }
if (data.containsKey('duration_in_seconds')) { if (data.containsKey('duration_ms')) {
context.handle( context.handle(
_durationInSecondsMeta, _durationMsMeta,
durationInSeconds.isAcceptableOrUnknown( durationMs.isAcceptableOrUnknown(data['duration_ms']!, _durationMsMeta),
data['duration_in_seconds']!,
_durationInSecondsMeta,
),
); );
} }
if (data.containsKey('id')) { if (data.containsKey('id')) {
@@ -733,9 +730,9 @@ class $TrashedLocalAssetEntityTable extends i3.TrashedLocalAssetEntity
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}height'], data['${effectivePrefix}height'],
), ),
durationInSeconds: attachedDatabase.typeMapping.read( durationMs: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int, i0.DriftSqlType.int,
data['${effectivePrefix}duration_in_seconds'], data['${effectivePrefix}duration_ms'],
), ),
id: attachedDatabase.typeMapping.read( id: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, i0.DriftSqlType.string,
@@ -800,7 +797,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
final DateTime updatedAt; final DateTime updatedAt;
final int? width; final int? width;
final int? height; final int? height;
final int? durationInSeconds; final int? durationMs;
final String id; final String id;
final String albumId; final String albumId;
final String? checksum; final String? checksum;
@@ -815,7 +812,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
required this.updatedAt, required this.updatedAt,
this.width, this.width,
this.height, this.height,
this.durationInSeconds, this.durationMs,
required this.id, required this.id,
required this.albumId, required this.albumId,
this.checksum, this.checksum,
@@ -841,8 +838,8 @@ class TrashedLocalAssetEntityData extends i0.DataClass
if (!nullToAbsent || height != null) { if (!nullToAbsent || height != null) {
map['height'] = i0.Variable<int>(height); map['height'] = i0.Variable<int>(height);
} }
if (!nullToAbsent || durationInSeconds != null) { if (!nullToAbsent || durationMs != null) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds); map['duration_ms'] = i0.Variable<int>(durationMs);
} }
map['id'] = i0.Variable<String>(id); map['id'] = i0.Variable<String>(id);
map['album_id'] = i0.Variable<String>(albumId); map['album_id'] = i0.Variable<String>(albumId);
@@ -880,7 +877,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
width: serializer.fromJson<int?>(json['width']), width: serializer.fromJson<int?>(json['width']),
height: serializer.fromJson<int?>(json['height']), height: serializer.fromJson<int?>(json['height']),
durationInSeconds: serializer.fromJson<int?>(json['durationInSeconds']), durationMs: serializer.fromJson<int?>(json['durationMs']),
id: serializer.fromJson<String>(json['id']), id: serializer.fromJson<String>(json['id']),
albumId: serializer.fromJson<String>(json['albumId']), albumId: serializer.fromJson<String>(json['albumId']),
checksum: serializer.fromJson<String?>(json['checksum']), checksum: serializer.fromJson<String?>(json['checksum']),
@@ -905,7 +902,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'width': serializer.toJson<int?>(width), 'width': serializer.toJson<int?>(width),
'height': serializer.toJson<int?>(height), 'height': serializer.toJson<int?>(height),
'durationInSeconds': serializer.toJson<int?>(durationInSeconds), 'durationMs': serializer.toJson<int?>(durationMs),
'id': serializer.toJson<String>(id), 'id': serializer.toJson<String>(id),
'albumId': serializer.toJson<String>(albumId), 'albumId': serializer.toJson<String>(albumId),
'checksum': serializer.toJson<String?>(checksum), 'checksum': serializer.toJson<String?>(checksum),
@@ -929,7 +926,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> width = const i0.Value.absent(), i0.Value<int?> width = const i0.Value.absent(),
i0.Value<int?> height = const i0.Value.absent(), i0.Value<int?> height = const i0.Value.absent(),
i0.Value<int?> durationInSeconds = const i0.Value.absent(), i0.Value<int?> durationMs = const i0.Value.absent(),
String? id, String? id,
String? albumId, String? albumId,
i0.Value<String?> checksum = const i0.Value.absent(), i0.Value<String?> checksum = const i0.Value.absent(),
@@ -944,9 +941,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width.present ? width.value : this.width, width: width.present ? width.value : this.width,
height: height.present ? height.value : this.height, height: height.present ? height.value : this.height,
durationInSeconds: durationInSeconds.present durationMs: durationMs.present ? durationMs.value : this.durationMs,
? durationInSeconds.value
: this.durationInSeconds,
id: id ?? this.id, id: id ?? this.id,
albumId: albumId ?? this.albumId, albumId: albumId ?? this.albumId,
checksum: checksum.present ? checksum.value : this.checksum, checksum: checksum.present ? checksum.value : this.checksum,
@@ -965,9 +960,9 @@ class TrashedLocalAssetEntityData extends i0.DataClass
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
width: data.width.present ? data.width.value : this.width, width: data.width.present ? data.width.value : this.width,
height: data.height.present ? data.height.value : this.height, height: data.height.present ? data.height.value : this.height,
durationInSeconds: data.durationInSeconds.present durationMs: data.durationMs.present
? data.durationInSeconds.value ? data.durationMs.value
: this.durationInSeconds, : this.durationMs,
id: data.id.present ? data.id.value : this.id, id: data.id.present ? data.id.value : this.id,
albumId: data.albumId.present ? data.albumId.value : this.albumId, albumId: data.albumId.present ? data.albumId.value : this.albumId,
checksum: data.checksum.present ? data.checksum.value : this.checksum, checksum: data.checksum.present ? data.checksum.value : this.checksum,
@@ -993,7 +988,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('albumId: $albumId, ') ..write('albumId: $albumId, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
@@ -1013,7 +1008,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
updatedAt, updatedAt,
width, width,
height, height,
durationInSeconds, durationMs,
id, id,
albumId, albumId,
checksum, checksum,
@@ -1032,7 +1027,7 @@ class TrashedLocalAssetEntityData extends i0.DataClass
other.updatedAt == this.updatedAt && other.updatedAt == this.updatedAt &&
other.width == this.width && other.width == this.width &&
other.height == this.height && other.height == this.height &&
other.durationInSeconds == this.durationInSeconds && other.durationMs == this.durationMs &&
other.id == this.id && other.id == this.id &&
other.albumId == this.albumId && other.albumId == this.albumId &&
other.checksum == this.checksum && other.checksum == this.checksum &&
@@ -1050,7 +1045,7 @@ class TrashedLocalAssetEntityCompanion
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> width; final i0.Value<int?> width;
final i0.Value<int?> height; final i0.Value<int?> height;
final i0.Value<int?> durationInSeconds; final i0.Value<int?> durationMs;
final i0.Value<String> id; final i0.Value<String> id;
final i0.Value<String> albumId; final i0.Value<String> albumId;
final i0.Value<String?> checksum; final i0.Value<String?> checksum;
@@ -1065,7 +1060,7 @@ class TrashedLocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.albumId = const i0.Value.absent(), this.albumId = const i0.Value.absent(),
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
@@ -1081,7 +1076,7 @@ class TrashedLocalAssetEntityCompanion
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.width = const i0.Value.absent(), this.width = const i0.Value.absent(),
this.height = const i0.Value.absent(), this.height = const i0.Value.absent(),
this.durationInSeconds = const i0.Value.absent(), this.durationMs = const i0.Value.absent(),
required String id, required String id,
required String albumId, required String albumId,
this.checksum = const i0.Value.absent(), this.checksum = const i0.Value.absent(),
@@ -1101,7 +1096,7 @@ class TrashedLocalAssetEntityCompanion
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? width, i0.Expression<int>? width,
i0.Expression<int>? height, i0.Expression<int>? height,
i0.Expression<int>? durationInSeconds, i0.Expression<int>? durationMs,
i0.Expression<String>? id, i0.Expression<String>? id,
i0.Expression<String>? albumId, i0.Expression<String>? albumId,
i0.Expression<String>? checksum, i0.Expression<String>? checksum,
@@ -1117,7 +1112,7 @@ class TrashedLocalAssetEntityCompanion
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (width != null) 'width': width, if (width != null) 'width': width,
if (height != null) 'height': height, if (height != null) 'height': height,
if (durationInSeconds != null) 'duration_in_seconds': durationInSeconds, if (durationMs != null) 'duration_ms': durationMs,
if (id != null) 'id': id, if (id != null) 'id': id,
if (albumId != null) 'album_id': albumId, if (albumId != null) 'album_id': albumId,
if (checksum != null) 'checksum': checksum, if (checksum != null) 'checksum': checksum,
@@ -1135,7 +1130,7 @@ class TrashedLocalAssetEntityCompanion
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? width, i0.Value<int?>? width,
i0.Value<int?>? height, i0.Value<int?>? height,
i0.Value<int?>? durationInSeconds, i0.Value<int?>? durationMs,
i0.Value<String>? id, i0.Value<String>? id,
i0.Value<String>? albumId, i0.Value<String>? albumId,
i0.Value<String?>? checksum, i0.Value<String?>? checksum,
@@ -1151,7 +1146,7 @@ class TrashedLocalAssetEntityCompanion
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
width: width ?? this.width, width: width ?? this.width,
height: height ?? this.height, height: height ?? this.height,
durationInSeconds: durationInSeconds ?? this.durationInSeconds, durationMs: durationMs ?? this.durationMs,
id: id ?? this.id, id: id ?? this.id,
albumId: albumId ?? this.albumId, albumId: albumId ?? this.albumId,
checksum: checksum ?? this.checksum, checksum: checksum ?? this.checksum,
@@ -1185,8 +1180,8 @@ class TrashedLocalAssetEntityCompanion
if (height.present) { if (height.present) {
map['height'] = i0.Variable<int>(height.value); map['height'] = i0.Variable<int>(height.value);
} }
if (durationInSeconds.present) { if (durationMs.present) {
map['duration_in_seconds'] = i0.Variable<int>(durationInSeconds.value); map['duration_ms'] = i0.Variable<int>(durationMs.value);
} }
if (id.present) { if (id.present) {
map['id'] = i0.Variable<String>(id.value); map['id'] = i0.Variable<String>(id.value);
@@ -1227,7 +1222,7 @@ class TrashedLocalAssetEntityCompanion
..write('updatedAt: $updatedAt, ') ..write('updatedAt: $updatedAt, ')
..write('width: $width, ') ..write('width: $width, ')
..write('height: $height, ') ..write('height: $height, ')
..write('durationInSeconds: $durationInSeconds, ') ..write('durationMs: $durationMs, ')
..write('id: $id, ') ..write('id: $id, ')
..write('albumId: $albumId, ') ..write('albumId: $albumId, ')
..write('checksum: $checksum, ') ..write('checksum: $checksum, ')
@@ -1,5 +1,3 @@
// ignore_for_file: experimental_member_use
import 'dart:async'; import 'dart:async';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
@@ -12,6 +10,7 @@ import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/memory.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory.entity.dart';
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
@@ -20,10 +19,12 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.dart'; import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.dart';
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart'; import 'package:immich_mobile/infrastructure/entities/stack.entity.dart';
import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.dart';
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
import 'package:immich_mobile/infrastructure/repositories/db.repository.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.drift.dart';
@@ -83,7 +84,7 @@ class Drift extends $Drift {
} }
@override @override
int get schemaVersion => 22; int get schemaVersion => 24;
@override @override
MigrationStrategy get migration => MigrationStrategy( MigrationStrategy get migration => MigrationStrategy(
@@ -224,6 +225,31 @@ class Drift extends $Drift {
await m.createTable(v22.assetEditEntity); await m.createTable(v22.assetEditEntity);
await m.createIndex(v22.idxAssetEditAssetId); await m.createIndex(v22.idxAssetEditAssetId);
}, },
from22To23: (m, v23) async {
await m.renameColumn(v23.localAssetEntity, 'duration_in_seconds', v23.localAssetEntity.durationMs);
await m.renameColumn(v23.remoteAssetEntity, 'duration_in_seconds', v23.remoteAssetEntity.durationMs);
await m.renameColumn(
v23.trashedLocalAssetEntity,
'duration_in_seconds',
v23.trashedLocalAssetEntity.durationMs,
);
await localAssetEntity.update().write(
LocalAssetEntityCompanion.custom(durationMs: v23.localAssetEntity.durationMs * const Constant(1000)),
);
await remoteAssetEntity.update().write(
RemoteAssetEntityCompanion.custom(durationMs: v23.remoteAssetEntity.durationMs * const Constant(1000)),
);
await trashedLocalAssetEntity.update().write(
TrashedLocalAssetEntityCompanion.custom(
durationMs: v23.trashedLocalAssetEntity.durationMs * const Constant(1000),
),
);
},
from23To24: (m, v24) async {
await customStatement('DROP INDEX IF EXISTS idx_remote_album_owner_id');
await m.alterTable(TableMigration(v24.remoteAlbumEntity));
},
), ),
); );
@@ -105,7 +105,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
localAlbumEntity, localAlbumEntity,
localAlbumAssetEntity, localAlbumAssetEntity,
i7.idxLocalAlbumAssetAlbumAsset, i7.idxLocalAlbumAssetAlbumAsset,
i5.idxRemoteAlbumOwnerId,
i4.idxLocalAssetChecksum, i4.idxLocalAssetChecksum,
i4.idxLocalAssetCloudId, i4.idxLocalAssetCloudId,
i3.idxStackPrimaryAssetId, i3.idxStackPrimaryAssetId,
@@ -160,15 +159,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
), ),
result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)], result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)],
), ),
i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName(
'user_entity',
limitUpdateKind: i0.UpdateKind.delete,
),
result: [
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
],
),
i0.WritePropagation( i0.WritePropagation(
on: i0.TableUpdateQuery.onTableName( on: i0.TableUpdateQuery.onTableName(
'remote_asset_entity', 'remote_asset_entity',
File diff suppressed because it is too large Load Diff
@@ -297,7 +297,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
updatedAt: Value(asset.updatedAt), updatedAt: Value(asset.updatedAt),
width: Value(asset.width), width: Value(asset.width),
height: Value(asset.height), height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds), durationMs: Value(asset.durationMs),
id: asset.id, id: asset.id,
orientation: Value(asset.orientation), orientation: Value(asset.orientation),
isFavorite: Value(asset.isFavorite), isFavorite: Value(asset.isFavorite),
@@ -329,7 +329,7 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
updatedAt: Value(asset.updatedAt), updatedAt: Value(asset.updatedAt),
width: Value(asset.width), width: Value(asset.width),
height: Value(asset.height), height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds), durationMs: Value(asset.durationMs),
id: asset.id, id: asset.id,
checksum: const Value(null), checksum: const Value(null),
orientation: Value(asset.orientation), orientation: Value(asset.orientation),
@@ -109,31 +109,40 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
return query.map((localAlbum) => localAlbum.toDto()).get(); return query.map((localAlbum) => localAlbum.toDto()).get();
} }
Future<Map<String, List<LocalAsset>>> getAssetsFromBackupAlbums(Iterable<String> checksums) async { Future<Map<String, List<LocalAsset>>> getAssetsFromBackupAlbums(Iterable<String> remoteIds) async {
if (checksums.isEmpty) { if (remoteIds.isEmpty) {
return {}; return {};
} }
final result = <String, List<LocalAsset>>{}; final result = <String, List<LocalAsset>>{};
for (final slice in checksums.toSet().slices(kDriftMaxChunk)) { for (final slice in remoteIds.toSet().slices(kDriftMaxChunk)) {
final rows = final rows =
await (_db.select(_db.localAlbumAssetEntity).join([ await (_db.select(_db.localAlbumAssetEntity).join([
innerJoin(_db.localAlbumEntity, _db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id)), innerJoin(
_db.localAlbumEntity,
_db.localAlbumAssetEntity.albumId.equalsExp(_db.localAlbumEntity.id),
useColumns: false,
),
innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)), innerJoin(_db.localAssetEntity, _db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id)),
innerJoin(
_db.remoteAssetEntity,
_db.localAssetEntity.checksum.equalsExp(_db.remoteAssetEntity.checksum),
useColumns: false,
),
])..where( ])..where(
_db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) & _db.localAlbumEntity.backupSelection.equalsValue(BackupSelection.selected) &
_db.localAssetEntity.checksum.isIn(slice), _db.remoteAssetEntity.id.isIn(slice),
)) ))
.get(); .get();
for (final row in rows) { for (final row in rows) {
final albumId = row.readTable(_db.localAlbumAssetEntity).albumId; final albumId = row.readTable(_db.localAlbumAssetEntity).albumId;
final assetData = row.readTable(_db.localAssetEntity); final asset = row.readTable(_db.localAssetEntity).toDto();
final asset = assetData.toDto();
(result[albumId] ??= <LocalAsset>[]).add(asset); (result[albumId] ??= <LocalAsset>[]).add(asset);
} }
} }
return result; return result;
} }
@@ -32,17 +32,23 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false, useColumns: false,
), ),
leftOuterJoin(_db.userEntity, _db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId), useColumns: false),
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false, useColumns: false,
), ),
innerJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
useColumns: false,
),
]); ]);
query query
..where(_db.remoteAssetEntity.deletedAt.isNull()) ..where(_db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount]) ..addColumns([assetCount])
..addColumns([_db.userEntity.name]) ..addColumns([_db.userEntity.name, _db.userEntity.id])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]); ..groupBy([_db.remoteAlbumEntity.id]);
@@ -63,6 +69,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
.readTable(_db.remoteAlbumEntity) .readTable(_db.remoteAlbumEntity)
.toDto( .toDto(
assetCount: row.read(assetCount) ?? 0, assetCount: row.read(assetCount) ?? 0,
ownerId: row.read(_db.userEntity.id)!,
ownerName: row.read(_db.userEntity.name)!, ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
), ),
@@ -85,20 +92,22 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false, useColumns: false,
), ),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false, useColumns: false,
), ),
innerJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
useColumns: false,
),
]) ])
..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull()) ..where(_db.remoteAlbumEntity.id.equals(albumId) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount]) ..addColumns([assetCount])
..addColumns([_db.userEntity.name]) ..addColumns([_db.userEntity.name, _db.userEntity.id])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]); ..groupBy([_db.remoteAlbumEntity.id]);
@@ -108,6 +117,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
.readTable(_db.remoteAlbumEntity) .readTable(_db.remoteAlbumEntity)
.toDto( .toDto(
assetCount: row.read(assetCount) ?? 0, assetCount: row.read(assetCount) ?? 0,
ownerId: row.read(_db.userEntity.id)!,
ownerName: row.read(_db.userEntity.name)!, ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
), ),
@@ -116,12 +126,29 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
} }
Future<RemoteAlbum?> getByName(String albumName, String ownerId) { Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
final query = _db.remoteAlbumEntity.select() final query = _db.remoteAlbumEntity.select().join([
..where((row) => row.name.equals(albumName) & row.ownerId.equals(ownerId)) innerJoin(
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]) _db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
_db.remoteAlbumUserEntity.userId.equals(ownerId) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
useColumns: false,
),
]);
query
..addColumns([_db.remoteAlbumUserEntity.userId])
..where(_db.remoteAlbumEntity.name.equals(albumName))
..orderBy([OrderingTerm.desc(_db.remoteAlbumEntity.createdAt)])
..limit(1); ..limit(1);
return query.map((row) => row.toDto(ownerName: '', isShared: false)).getSingleOrNull(); return query
.map(
(row) => row
.readTable(_db.remoteAlbumEntity)
.toDto(ownerId: row.read(_db.remoteAlbumUserEntity.userId)!, ownerName: '', isShared: false),
)
.getSingleOrNull();
} }
Future<void> create(RemoteAlbum album, List<String> assetIds) async { Future<void> create(RemoteAlbum album, List<String> assetIds) async {
@@ -129,7 +156,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
final entity = RemoteAlbumEntityCompanion( final entity = RemoteAlbumEntityCompanion(
id: Value(album.id), id: Value(album.id),
name: Value(album.name), name: Value(album.name),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt), createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt), updatedAt: Value(album.updatedAt),
description: Value(album.description), description: Value(album.description),
@@ -140,6 +166,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
await _db.remoteAlbumEntity.insertOne(entity); await _db.remoteAlbumEntity.insertOne(entity);
await _db.remoteAlbumUserEntity.insertOne(
RemoteAlbumUserEntityCompanion(
albumId: Value(album.id),
userId: Value(album.ownerId),
role: const Value(AlbumUserRole.owner),
),
);
if (assetIds.isNotEmpty) { if (assetIds.isNotEmpty) {
final albumAssets = assetIds.map( final albumAssets = assetIds.map(
(assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)), (assetId) => RemoteAlbumAssetEntityCompanion(albumId: Value(album.id), assetId: Value(assetId)),
@@ -157,7 +191,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
RemoteAlbumEntityCompanion( RemoteAlbumEntityCompanion(
id: Value(album.id), id: Value(album.id),
name: Value(album.name), name: Value(album.name),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt), createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt), updatedAt: Value(album.updatedAt),
description: Value(album.description), description: Value(album.description),
@@ -197,7 +230,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
Future<List<UserDto>> getSharedUsers(String albumId) async { Future<List<UserDto>> getSharedUsers(String albumId) async {
final albumUserRows = await (_db.select( final albumUserRows = await (_db.select(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
)..where((row) => row.albumId.equals(albumId))).get(); )..where((row) => row.albumId.equals(albumId) & row.role.isNotValue(AlbumUserRole.owner.index))).get();
if (albumUserRows.isEmpty) { if (albumUserRows.isEmpty) {
return []; return [];
@@ -295,19 +328,21 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false, useColumns: false,
), ),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false, useColumns: false,
), ),
innerJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
useColumns: false,
),
]) ])
..where(_db.remoteAlbumEntity.id.equals(albumId)) ..where(_db.remoteAlbumEntity.id.equals(albumId))
..addColumns([_db.userEntity.name]) ..addColumns([_db.userEntity.name, _db.userEntity.id])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..groupBy([_db.remoteAlbumEntity.id]); ..groupBy([_db.remoteAlbumEntity.id]);
@@ -315,6 +350,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
final album = row final album = row
.readTable(_db.remoteAlbumEntity) .readTable(_db.remoteAlbumEntity)
.toDto( .toDto(
ownerId: row.read(_db.userEntity.id)!,
ownerName: row.read(_db.userEntity.name)!, ownerName: row.read(_db.userEntity.name)!,
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
); );
@@ -355,6 +391,37 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
return _db.managers.remoteAlbumEntity.count(); return _db.managers.remoteAlbumEntity.count();
} }
Future<UserDto> getOwner(String albumId) {
final query =
_db.userEntity.select().join([
innerJoin(
_db.remoteAlbumUserEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId),
useColumns: false,
),
])..where(
_db.remoteAlbumUserEntity.albumId.equals(albumId) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
);
return query
.map(
(row) => UserDto(
id: row.read(_db.userEntity.id)!,
email: row.read(_db.userEntity.email)!,
name: row.read(_db.userEntity.name)!,
memoryEnabled: true,
inTimeline: false,
isPartnerSharedBy: false,
isPartnerSharedWith: false,
profileChangedAt: row.read(_db.userEntity.profileChangedAt)!,
hasProfileImage: row.read(_db.userEntity.hasProfileImage)!,
avatarColor: AvatarColor.values[row.read(_db.userEntity.avatarColor)!],
),
)
.getSingle();
}
Future<List<String>> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async { Future<List<String>> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async {
// Find remote asset ids that: // Find remote asset ids that:
// 1. Belong to the provided local album (via local_album_asset_entity) // 1. Belong to the provided local album (via local_album_asset_entity)
@@ -416,21 +483,23 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
_db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId), _db.remoteAssetEntity.id.equalsExp(_db.remoteAlbumAssetEntity.assetId),
useColumns: false, useColumns: false,
), ),
leftOuterJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumEntity.ownerId),
useColumns: false,
),
leftOuterJoin( leftOuterJoin(
_db.remoteAlbumUserEntity, _db.remoteAlbumUserEntity,
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id), _db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id),
useColumns: false, useColumns: false,
), ),
innerJoin(
_db.userEntity,
_db.userEntity.id.equalsExp(_db.remoteAlbumUserEntity.userId) &
_db.remoteAlbumUserEntity.albumId.equalsExp(_db.remoteAlbumEntity.id) &
_db.remoteAlbumUserEntity.role.equalsValue(AlbumUserRole.owner),
useColumns: false,
),
]) ])
..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull()) ..where(_db.remoteAlbumEntity.id.isIn(albumIds) & _db.remoteAssetEntity.deletedAt.isNull())
..addColumns([assetCount]) ..addColumns([assetCount])
..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)]) ..addColumns([_db.remoteAlbumUserEntity.userId.count(distinct: true)])
..addColumns([_db.userEntity.name]) ..addColumns([_db.userEntity.name, _db.userEntity.id])
..groupBy([_db.remoteAlbumEntity.id]); ..groupBy([_db.remoteAlbumEntity.id]);
return query return query
@@ -438,6 +507,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
(row) => row (row) => row
.readTable(_db.remoteAlbumEntity) .readTable(_db.remoteAlbumEntity)
.toDto( .toDto(
ownerId: row.read(_db.userEntity.id)!,
ownerName: row.read(_db.userEntity.name) ?? '', ownerName: row.read(_db.userEntity.name) ?? '',
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0, isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
assetCount: row.read(assetCount) ?? 0, assetCount: row.read(assetCount) ?? 0,
@@ -448,7 +518,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
} }
extension on RemoteAlbumEntityData { extension on RemoteAlbumEntityData {
RemoteAlbum toDto({int assetCount = 0, required String ownerName, required bool isShared}) { RemoteAlbum toDto({int assetCount = 0, required String ownerName, required String ownerId, required bool isShared}) {
return RemoteAlbum( return RemoteAlbum(
id: id, id: id,
name: name, name: name,
@@ -53,7 +53,10 @@ class SyncApiRepository {
SyncRequestType.partnersV1, SyncRequestType.partnersV1,
SyncRequestType.partnerAssetsV1, SyncRequestType.partnerAssetsV1,
SyncRequestType.partnerAssetExifsV1, SyncRequestType.partnerAssetExifsV1,
SyncRequestType.albumsV1, if (serverVersion < const SemVer(major: 3, minor: 0, patch: 0))
SyncRequestType.albumsV1
else
SyncRequestType.albumsV2,
SyncRequestType.albumUsersV1, SyncRequestType.albumUsersV1,
SyncRequestType.albumAssetsV1, SyncRequestType.albumAssetsV1,
SyncRequestType.albumAssetExifsV1, SyncRequestType.albumAssetExifsV1,
@@ -162,6 +165,7 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson, SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson, SyncEntityType.partnerAssetExifBackfillV1: SyncAssetExifV1.fromJson,
SyncEntityType.albumV1: SyncAlbumV1.fromJson, SyncEntityType.albumV1: SyncAlbumV1.fromJson,
SyncEntityType.albumV2: SyncAlbumV2.fromJson,
SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson, SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson, SyncEntityType.albumUserBackfillV1: SyncAlbumUserV1.fromJson,
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/asset_edit.model.dart';
import 'package:immich_mobile/domain/models/memory.model.dart'; import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/models/user_metadata.model.dart'; import 'package:immich_mobile/domain/models/user_metadata.model.dart';
import 'package:immich_mobile/extensions/string_extensions.dart';
import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/asset_edit.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart';
import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/auth_user.entity.drift.dart';
@@ -29,7 +30,7 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey, AssetEditAction; import 'package:openapi/api.dart' as api show AssetVisibility, AlbumUserRole, UserMetadataKey, AssetEditAction;
import 'package:openapi/api.dart' hide AlbumUserRole, UserMetadataKey, AssetEditAction, AssetVisibility; import 'package:openapi/api.dart' hide UserMetadataKey, AssetEditAction, AssetVisibility, AlbumUserRole;
class SyncStreamRepository extends DriftDatabaseRepository { class SyncStreamRepository extends DriftDatabaseRepository {
final Logger _logger = Logger('DriftSyncStreamRepository'); final Logger _logger = Logger('DriftSyncStreamRepository');
@@ -190,7 +191,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
type: Value(asset.type.toAssetType()), type: Value(asset.type.toAssetType()),
createdAt: Value.absentIfNull(asset.fileCreatedAt), createdAt: Value.absentIfNull(asset.fileCreatedAt),
updatedAt: Value.absentIfNull(asset.fileModifiedAt), updatedAt: Value.absentIfNull(asset.fileModifiedAt),
durationInSeconds: Value(asset.duration?.toDuration()?.inSeconds ?? 0), durationMs: Value(asset.duration?.toDuration()?.inMilliseconds ?? 0),
checksum: Value(asset.checksum), checksum: Value(asset.checksum),
isFavorite: Value(asset.isFavorite), isFavorite: Value(asset.isFavorite),
ownerId: Value(asset.ownerId), ownerId: Value(asset.ownerId),
@@ -396,6 +397,47 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async { Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
try {
await _db.transaction(() async {
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumEntityCompanion(
name: Value(album.name),
description: Value(album.description),
isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order.toAlbumAssetOrder()),
thumbnailAssetId: Value(album.thumbnailAssetId),
createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt),
);
batch.insert(
_db.remoteAlbumEntity,
companion.copyWith(id: Value(album.id)),
onConflict: DoUpdate((_) => companion),
);
}
});
await _db.batch((batch) {
for (final album in data) {
final companion = RemoteAlbumUserEntityCompanion(
albumId: Value(album.id),
userId: Value(album.ownerId),
role: const Value(AlbumUserRole.owner),
);
batch.insert(_db.remoteAlbumUserEntity, companion, onConflict: DoUpdate((_) => companion));
}
});
});
} catch (error, stack) {
_logger.severe('Error: updateAlbumsV1', error, stack);
rethrow;
}
}
Future<void> updateAlbumsV2(Iterable<SyncAlbumV2> data) async {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final album in data) { for (final album in data) {
@@ -405,7 +447,6 @@ class SyncStreamRepository extends DriftDatabaseRepository {
isActivityEnabled: Value(album.isActivityEnabled), isActivityEnabled: Value(album.isActivityEnabled),
order: Value(album.order.toAlbumAssetOrder()), order: Value(album.order.toAlbumAssetOrder()),
thumbnailAssetId: Value(album.thumbnailAssetId), thumbnailAssetId: Value(album.thumbnailAssetId),
ownerId: Value(album.ownerId),
createdAt: Value(album.createdAt), createdAt: Value(album.createdAt),
updatedAt: Value(album.updatedAt), updatedAt: Value(album.updatedAt),
); );
@@ -418,7 +459,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
} }
}); });
} catch (error, stack) { } catch (error, stack) {
_logger.severe('Error: updateAlbumsV1', error, stack); _logger.severe('Error: updateAlbumsV2', error, stack);
rethrow; rethrow;
} }
} }
@@ -820,6 +861,7 @@ extension on api.AlbumUserRole {
AlbumUserRole toAlbumUserRole() => switch (this) { AlbumUserRole toAlbumUserRole() => switch (this) {
api.AlbumUserRole.editor => AlbumUserRole.editor, api.AlbumUserRole.editor => AlbumUserRole.editor,
api.AlbumUserRole.viewer => AlbumUserRole.viewer, api.AlbumUserRole.viewer => AlbumUserRole.viewer,
api.AlbumUserRole.owner => AlbumUserRole.owner,
_ => throw Exception('Unknown AlbumUserRole value: $this'), _ => throw Exception('Unknown AlbumUserRole value: $this'),
}; };
} }
@@ -843,18 +885,6 @@ extension on api.UserMetadataKey {
}; };
} }
extension on String {
Duration? toDuration() {
try {
final parts = split(':').map((e) => double.parse(e).toInt()).toList(growable: false);
return Duration(hours: parts[0], minutes: parts[1], seconds: parts[2]);
} catch (_) {
return null;
}
}
}
extension on UserAvatarColor { extension on UserAvatarColor {
AvatarColor? toAvatarColor() => AvatarColor.values.firstWhereOrNull((c) => c.name == value); AvatarColor? toAvatarColor() => AvatarColor.values.firstWhereOrNull((c) => c.name == value);
} }
@@ -83,7 +83,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
width: row.width, width: row.width,
height: row.height, height: row.height,
isFavorite: row.isFavorite, isFavorite: row.isFavorite,
durationInSeconds: row.durationInSeconds, durationMs: row.durationMs,
livePhotoVideoId: row.livePhotoVideoId, livePhotoVideoId: row.livePhotoVideoId,
stackId: row.stackId, stackId: row.stackId,
isEdited: row.isEdited, isEdited: row.isEdited,
@@ -99,7 +99,7 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
width: row.width, width: row.width,
height: row.height, height: row.height,
isFavorite: row.isFavorite, isFavorite: row.isFavorite,
durationInSeconds: row.durationInSeconds, durationMs: row.durationMs,
orientation: row.orientation, orientation: row.orientation,
playbackStyle: AssetPlaybackStyle.values[row.playbackStyle], playbackStyle: AssetPlaybackStyle.values[row.playbackStyle],
cloudId: row.iCloudId, cloudId: row.iCloudId,
@@ -82,7 +82,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
updatedAt: Value(item.asset.updatedAt), updatedAt: Value(item.asset.updatedAt),
width: Value(item.asset.width), width: Value(item.asset.width),
height: Value(item.asset.height), height: Value(item.asset.height),
durationInSeconds: Value(item.asset.durationInSeconds), durationMs: Value(item.asset.durationMs),
isFavorite: Value(item.asset.isFavorite), isFavorite: Value(item.asset.isFavorite),
orientation: Value(item.asset.orientation), orientation: Value(item.asset.orientation),
playbackStyle: Value(item.asset.playbackStyle), playbackStyle: Value(item.asset.playbackStyle),
@@ -145,7 +145,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
type: Value(asset.type), type: Value(asset.type),
width: Value(asset.width), width: Value(asset.width),
height: Value(asset.height), height: Value(asset.height),
durationInSeconds: Value(asset.durationInSeconds), durationMs: Value(asset.durationMs),
isFavorite: Value(asset.isFavorite), isFavorite: Value(asset.isFavorite),
orientation: Value(asset.orientation), orientation: Value(asset.orientation),
playbackStyle: Value(asset.playbackStyle), playbackStyle: Value(asset.playbackStyle),
@@ -193,7 +193,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
updatedAt: Value(e.updatedAt), updatedAt: Value(e.updatedAt),
width: Value(e.width), width: Value(e.width),
height: Value(e.height), height: Value(e.height),
durationInSeconds: Value(e.durationInSeconds), durationMs: Value(e.durationMs),
checksum: Value(e.checksum), checksum: Value(e.checksum),
isFavorite: Value(e.isFavorite), isFavorite: Value(e.isFavorite),
orientation: Value(e.orientation), orientation: Value(e.orientation),
@@ -244,7 +244,7 @@ class DriftTrashedLocalAssetRepository extends DriftDatabaseRepository {
updatedAt: Value(e.asset.updatedAt), updatedAt: Value(e.asset.updatedAt),
width: Value(e.asset.width), width: Value(e.asset.width),
height: Value(e.asset.height), height: Value(e.asset.height),
durationInSeconds: Value(e.asset.durationInSeconds), durationMs: Value(e.asset.durationMs),
checksum: Value(e.asset.checksum), checksum: Value(e.asset.checksum),
isFavorite: Value(e.asset.isFavorite), isFavorite: Value(e.asset.isFavorite),
orientation: Value(e.asset.orientation), orientation: Value(e.asset.orientation),
@@ -8,5 +8,5 @@ mixin AssetEntityMixin on Table {
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
IntColumn get width => integer().nullable()(); IntColumn get width => integer().nullable()();
IntColumn get height => integer().nullable()(); IntColumn get height => integer().nullable()();
IntColumn get durationInSeconds => integer().nullable()(); IntColumn get durationMs => integer().nullable()();
} }
@@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/user.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart';
import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart'; import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart';
import 'package:immich_mobile/providers/infrastructure/partner.provider.dart'; import 'package:immich_mobile/providers/infrastructure/partner.provider.dart';

Some files were not shown because too many files have changed in this diff Show More