mirror of
https://github.com/immich-app/immich.git
synced 2025-12-13 17:23:10 +03:00
Compare commits
26 Commits
revert-sve
...
feat/sync-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e75d79bee3 | ||
|
|
04dbd8b3b1 | ||
|
|
e809ffe37b | ||
|
|
4b345e02ff | ||
|
|
8cf900bafa | ||
|
|
59a3f0f455 | ||
|
|
c5d99711f7 | ||
|
|
4c0a41723f | ||
|
|
f73511a754 | ||
|
|
e637387082 | ||
|
|
baad38f0e6 | ||
|
|
161147af51 | ||
|
|
cbdf5011f9 | ||
|
|
f0f1d279c4 | ||
|
|
5821f2fe61 | ||
|
|
7447d407eb | ||
|
|
3a796f6a3d | ||
|
|
d47e9a5db4 | ||
|
|
0a84783841 | ||
|
|
12febaf4a5 | ||
|
|
a2a5b7e758 | ||
|
|
b06f591b7a | ||
|
|
e76b6baf23 | ||
|
|
44b50bfa23 | ||
|
|
d90d22f44a | ||
|
|
3ec8e5b253 |
2
.github/workflows/build-mobile.yml
vendored
2
.github/workflows/build-mobile.yml
vendored
@@ -108,7 +108,7 @@ jobs:
|
|||||||
working-directory: ./mobile
|
working-directory: ./mobile
|
||||||
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
run: printf "%s" $KEY_JKS | base64 -d > android/key.jks
|
||||||
|
|
||||||
- uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
|
- uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
|
|||||||
2
.github/workflows/cli.yml
vendored
2
.github/workflows/cli.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -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@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
||||||
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@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/autobuild@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
||||||
|
|
||||||
# ℹ️ 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@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v4.31.7
|
||||||
with:
|
with:
|
||||||
category: '/language:${{matrix.language}}'
|
category: '/language:${{matrix.language}}'
|
||||||
|
|||||||
2
.github/workflows/docs-build.yml
vendored
2
.github/workflows/docs-build.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './docs/.nvmrc'
|
node-version-file: './docs/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
4
.github/workflows/fix-format.yml
vendored
4
.github/workflows/fix-format.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -32,7 +32,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
2
.github/workflows/merge-translations.yml
vendored
2
.github/workflows/merge-translations.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate_token
|
id: generate_token
|
||||||
if: ${{ inputs.skip != true }}
|
if: ${{ inputs.skip != true }}
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
|
|||||||
8
.github/workflows/prepare-release.yml
vendored
8
.github/workflows/prepare-release.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -144,7 +144,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@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
tag_name: ${{ env.IMMICH_VERSION }}
|
tag_name: ${{ env.IMMICH_VERSION }}
|
||||||
|
|||||||
6
.github/workflows/release-pr.yml
vendored
6
.github/workflows/release-pr.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -159,7 +159,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create PR
|
- name: Create PR
|
||||||
id: create-pr
|
id: create-pr
|
||||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9
|
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
commit-message: 'chore: release ${{ steps.bump-type.outputs.next }}'
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Generate a token
|
- name: Generate a token
|
||||||
id: generate-token
|
id: generate-token
|
||||||
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
|
uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
app-id: ${{ secrets.PUSH_O_MATIC_APP_ID }}
|
||||||
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
private-key: ${{ secrets.PUSH_O_MATIC_APP_KEY }}
|
||||||
@@ -80,7 +80,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@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ steps.version.outputs.result }}
|
tag_name: ${{ steps.version.outputs.result }}
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|||||||
2
.github/workflows/sdk.yml
vendored
2
.github/workflows/sdk.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
|||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
# Setup .npmrc file to publish to npm
|
||||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
- uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|||||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -121,7 +121,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -168,7 +168,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './cli/.nvmrc'
|
node-version-file: './cli/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -210,7 +210,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -254,7 +254,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -292,7 +292,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './web/.nvmrc'
|
node-version-file: './web/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -340,7 +340,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -387,7 +387,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -426,7 +426,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -481,7 +481,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './e2e/.nvmrc'
|
node-version-file: './e2e/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -617,7 +617,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './.github/.nvmrc'
|
node-version-file: './.github/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -668,7 +668,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
@@ -730,7 +730,7 @@ jobs:
|
|||||||
- name: Setup pnpm
|
- name: Setup pnpm
|
||||||
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
|
||||||
with:
|
with:
|
||||||
node-version-file: './server/.nvmrc'
|
node-version-file: './server/.nvmrc'
|
||||||
cache: 'pnpm'
|
cache: 'pnpm'
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
@@ -83,7 +83,7 @@ services:
|
|||||||
container_name: immich_prometheus
|
container_name: immich_prometheus
|
||||||
ports:
|
ports:
|
||||||
- 9090:9090
|
- 9090:9090
|
||||||
image: prom/prometheus@sha256:49214755b6153f90a597adcbff0252cc61069f8ab69ce8411285cd4a560e8038
|
image: prom/prometheus@sha256:d936808bdea528155c0154a922cd42fd75716b8bb7ba302641350f9f3eaeba09
|
||||||
volumes:
|
volumes:
|
||||||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
- prometheus-data:/prometheus
|
- prometheus-data:/prometheus
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ services:
|
|||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: immich_redis
|
container_name: immich_redis
|
||||||
image: docker.io/valkey/valkey:9@sha256:4503e204c900a00ad393bec83c8c7c4c76b0529cd629e23b34b52011aefd1d27
|
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: redis-cli ping || exit 1
|
test: redis-cli ping || exit 1
|
||||||
restart: always
|
restart: always
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Password login has been enabled.
|
|||||||
Disable Maintenance Mode
|
Disable Maintenance Mode
|
||||||
|
|
||||||
```
|
```
|
||||||
immich-admin disable-maintenace-mode
|
immich-admin disable-maintenance-mode
|
||||||
Maintenance mode has been disabled.
|
Maintenance mode has been disabled.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2042,6 +2042,7 @@
|
|||||||
"sync": "Sync",
|
"sync": "Sync",
|
||||||
"sync_albums": "Sync albums",
|
"sync_albums": "Sync albums",
|
||||||
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
|
"sync_albums_manual_subtitle": "Sync all uploaded videos and photos to the selected backup albums",
|
||||||
|
"sync_cloud_ids": "Sync Cloud IDs",
|
||||||
"sync_local": "Sync Local",
|
"sync_local": "Sync Local",
|
||||||
"sync_remote": "Sync Remote",
|
"sync_remote": "Sync Remote",
|
||||||
"sync_status": "Sync Status",
|
"sync_status": "Sync Status",
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ interface NativeSyncApi {
|
|||||||
fun hashAssets(assetIds: List<String>, allowNetworkAccess: Boolean, callback: (Result<List<HashResult>>) -> Unit)
|
fun hashAssets(assetIds: List<String>, allowNetworkAccess: Boolean, callback: (Result<List<HashResult>>) -> Unit)
|
||||||
fun cancelHashing()
|
fun cancelHashing()
|
||||||
fun getTrashedAssets(): Map<String, List<PlatformAsset>>
|
fun getTrashedAssets(): Map<String, List<PlatformAsset>>
|
||||||
|
fun getCloudIdForAssetIds(assetIds: List<String>): Map<String, String?>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** The codec used by NativeSyncApi. */
|
/** The codec used by NativeSyncApi. */
|
||||||
@@ -508,6 +509,23 @@ interface NativeSyncApi {
|
|||||||
channel.setMessageHandler(null)
|
channel.setMessageHandler(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getCloudIdForAssetIds$separatedMessageChannelSuffix", codec, taskQueue)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val assetIdsArg = args[0] as List<String>
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
listOf(api.getCloudIdForAssetIds(assetIdsArg))
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
MessagesPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,4 +316,10 @@ open class NativeSyncApiImplBase(context: Context) : ImmichPlugin() {
|
|||||||
hashTask?.cancel()
|
hashTask?.cancel()
|
||||||
hashTask = null
|
hashTask = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This method is only implemented on iOS; on Android, we do not have a concept of cloud IDs
|
||||||
|
@Suppress("unused", "UNUSED_PARAMETER")
|
||||||
|
fun getCloudIdForAssetIds(assetIds: List<String>): Map<String, String?> {
|
||||||
|
return emptyMap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
mobile/drift_schemas/main/drift_schema_v15.json
generated
Normal file
1
mobile/drift_schemas/main/drift_schema_v15.json
generated
Normal file
File diff suppressed because one or more lines are too long
@@ -377,6 +377,7 @@ protocol NativeSyncApi {
|
|||||||
func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void)
|
func hashAssets(assetIds: [String], allowNetworkAccess: Bool, completion: @escaping (Result<[HashResult], Error>) -> Void)
|
||||||
func cancelHashing() throws
|
func cancelHashing() throws
|
||||||
func getTrashedAssets() throws -> [String: [PlatformAsset]]
|
func getTrashedAssets() throws -> [String: [PlatformAsset]]
|
||||||
|
func getCloudIdForAssetIds(assetIds: [String]) throws -> [String: String?]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
|
||||||
@@ -560,5 +561,22 @@ class NativeSyncApiSetup {
|
|||||||
} else {
|
} else {
|
||||||
getTrashedAssetsChannel.setMessageHandler(nil)
|
getTrashedAssetsChannel.setMessageHandler(nil)
|
||||||
}
|
}
|
||||||
|
let getCloudIdForAssetIdsChannel = taskQueue == nil
|
||||||
|
? FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getCloudIdForAssetIds\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||||
|
: FlutterBasicMessageChannel(name: "dev.flutter.pigeon.immich_mobile.NativeSyncApi.getCloudIdForAssetIds\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec, taskQueue: taskQueue)
|
||||||
|
if let api = api {
|
||||||
|
getCloudIdForAssetIdsChannel.setMessageHandler { message, reply in
|
||||||
|
let args = message as! [Any?]
|
||||||
|
let assetIdsArg = args[0] as! [String]
|
||||||
|
do {
|
||||||
|
let result = try api.getCloudIdForAssetIds(assetIds: assetIdsArg)
|
||||||
|
reply(wrapResult(result))
|
||||||
|
} catch {
|
||||||
|
reply(wrapError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
getCloudIdForAssetIdsChannel.setMessageHandler(nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -390,4 +390,22 @@ class NativeSyncApiImpl: ImmichPlugin, NativeSyncApi, FlutterPlugin {
|
|||||||
return PHAsset.fetchAssets(in: album, options: options)
|
return PHAsset.fetchAssets(in: album, options: options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCloudIdForAssetIds(assetIds: [String]) throws -> [String : String?] {
|
||||||
|
guard #available(iOS 16, *) else {
|
||||||
|
return Dictionary(
|
||||||
|
uniqueKeysWithValues: assetIds.map { ($0, nil as String?) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mappings: [String: String?] = [:]
|
||||||
|
let result = PHPhotoLibrary.shared().cloudIdentifierMappings(forLocalIdentifiers: assetIds)
|
||||||
|
for (key, value) in result {
|
||||||
|
// Ignores invalid cloud ids of the format "GUID:ID:". Valid Ids are of the form "GUID:ID:HASH"
|
||||||
|
if let cloudId = try? value.get().stringValue, !cloudId.hasSuffix(":") {
|
||||||
|
mappings[key] = cloudId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mappings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
mobile/lib/domain/models/asset/asset_metadata.model.dart
Normal file
62
mobile/lib/domain/models/asset/asset_metadata.model.dart
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
enum RemoteAssetMetadataKey {
|
||||||
|
mobileApp("mobile-app");
|
||||||
|
|
||||||
|
final String key;
|
||||||
|
|
||||||
|
const RemoteAssetMetadataKey(this.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class RemoteAssetMetadataValue {
|
||||||
|
const RemoteAssetMetadataValue();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
class RemoteAssetMetadataItem {
|
||||||
|
final RemoteAssetMetadataKey key;
|
||||||
|
final RemoteAssetMetadataValue value;
|
||||||
|
|
||||||
|
const RemoteAssetMetadataItem({required this.key, required this.value});
|
||||||
|
|
||||||
|
Map<String, Object?> toJson() {
|
||||||
|
return {'key': key.key, 'value': value};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RemoteAssetMobileAppMetadata extends RemoteAssetMetadataValue {
|
||||||
|
final String? cloudId;
|
||||||
|
final String? createdAt;
|
||||||
|
final String? adjustmentTime;
|
||||||
|
final String? latitude;
|
||||||
|
final String? longitude;
|
||||||
|
|
||||||
|
const RemoteAssetMobileAppMetadata({
|
||||||
|
this.cloudId,
|
||||||
|
this.createdAt,
|
||||||
|
this.adjustmentTime,
|
||||||
|
this.latitude,
|
||||||
|
this.longitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final map = <String, Object?>{};
|
||||||
|
if (cloudId != null) {
|
||||||
|
map["iCloudId"] = cloudId;
|
||||||
|
}
|
||||||
|
if (createdAt != null) {
|
||||||
|
map["createdAt"] = createdAt;
|
||||||
|
}
|
||||||
|
if (adjustmentTime != null) {
|
||||||
|
map["adjustmentTime"] = adjustmentTime;
|
||||||
|
}
|
||||||
|
if (latitude != null) {
|
||||||
|
map["latitude"] = latitude;
|
||||||
|
}
|
||||||
|
if (longitude != null) {
|
||||||
|
map["longitude"] = longitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ part of 'base_asset.model.dart';
|
|||||||
class LocalAsset extends BaseAsset {
|
class LocalAsset extends BaseAsset {
|
||||||
final String id;
|
final String id;
|
||||||
final String? remoteAssetId;
|
final String? remoteAssetId;
|
||||||
|
final String? cloudId;
|
||||||
final int orientation;
|
final int orientation;
|
||||||
|
|
||||||
final DateTime? adjustmentTime;
|
final DateTime? adjustmentTime;
|
||||||
@@ -12,6 +13,7 @@ class LocalAsset extends BaseAsset {
|
|||||||
const LocalAsset({
|
const LocalAsset({
|
||||||
required this.id,
|
required this.id,
|
||||||
String? remoteId,
|
String? remoteId,
|
||||||
|
this.cloudId,
|
||||||
required super.name,
|
required super.name,
|
||||||
super.checksum,
|
super.checksum,
|
||||||
required super.type,
|
required super.type,
|
||||||
@@ -54,11 +56,13 @@ class LocalAsset extends BaseAsset {
|
|||||||
height: ${height ?? "<NA>"},
|
height: ${height ?? "<NA>"},
|
||||||
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
durationInSeconds: ${durationInSeconds ?? "<NA>"},
|
||||||
remoteId: ${remoteId ?? "<NA>"}
|
remoteId: ${remoteId ?? "<NA>"}
|
||||||
|
cloudId: ${cloudId ?? "<NA>"}
|
||||||
|
checksum: ${checksum ?? "<NA>"},
|
||||||
isFavorite: $isFavorite,
|
isFavorite: $isFavorite,
|
||||||
orientation: $orientation,
|
orientation: $orientation,
|
||||||
adjustmentTime: $adjustmentTime,
|
adjustmentTime: $adjustmentTime,
|
||||||
latitude: ${latitude ?? "<NA>"},
|
latitude: ${latitude ?? "<NA>"},
|
||||||
longitude: ${longitude ?? "<NA>"},
|
longitude: ${longitude ?? "<NA>"},
|
||||||
}''';
|
}''';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +73,7 @@ class LocalAsset extends BaseAsset {
|
|||||||
if (identical(this, other)) return true;
|
if (identical(this, other)) return true;
|
||||||
return super == other &&
|
return super == other &&
|
||||||
id == other.id &&
|
id == other.id &&
|
||||||
|
cloudId == other.cloudId &&
|
||||||
orientation == other.orientation &&
|
orientation == other.orientation &&
|
||||||
adjustmentTime == other.adjustmentTime &&
|
adjustmentTime == other.adjustmentTime &&
|
||||||
latitude == other.latitude &&
|
latitude == other.latitude &&
|
||||||
@@ -88,6 +93,7 @@ class LocalAsset extends BaseAsset {
|
|||||||
LocalAsset copyWith({
|
LocalAsset copyWith({
|
||||||
String? id,
|
String? id,
|
||||||
String? remoteId,
|
String? remoteId,
|
||||||
|
String? cloudId,
|
||||||
String? name,
|
String? name,
|
||||||
String? checksum,
|
String? checksum,
|
||||||
AssetType? type,
|
AssetType? type,
|
||||||
@@ -105,6 +111,7 @@ class LocalAsset extends BaseAsset {
|
|||||||
return LocalAsset(
|
return LocalAsset(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
remoteId: remoteId ?? this.remoteId,
|
remoteId: remoteId ?? this.remoteId,
|
||||||
|
cloudId: cloudId ?? this.cloudId,
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ class HashService {
|
|||||||
_log.info("Starting hashing of assets");
|
_log.info("Starting hashing of assets");
|
||||||
final Stopwatch stopwatch = Stopwatch()..start();
|
final Stopwatch stopwatch = Stopwatch()..start();
|
||||||
try {
|
try {
|
||||||
|
// Migrate hashes from cloud ID to local ID so we don't have to re-hash them
|
||||||
|
await _migrateHashes();
|
||||||
|
|
||||||
// Sorted by backupSelection followed by isCloud
|
// Sorted by backupSelection followed by isCloud
|
||||||
final localAlbums = await _localAlbumRepository.getBackupAlbums();
|
final localAlbums = await _localAlbumRepository.getBackupAlbums();
|
||||||
|
|
||||||
@@ -75,6 +78,15 @@ class HashService {
|
|||||||
_log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms");
|
_log.info("Hashing took - ${stopwatch.elapsedMilliseconds}ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _migrateHashes() async {
|
||||||
|
final hashMappings = await _localAssetRepository.getHashMappingFromCloudId();
|
||||||
|
if (hashMappings.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _localAssetRepository.updateHashes(hashMappings);
|
||||||
|
}
|
||||||
|
|
||||||
/// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB
|
/// Processes a list of [LocalAsset]s, storing their hash and updating the assets in the DB
|
||||||
/// with hash for those that were successfully hashed. Hashes are looked up in a table
|
/// with hash for those that were successfully hashed. Hashes are looked up in a table
|
||||||
/// [LocalAssetHashEntity] by local id. Only missing entries are newly hashed and added to the DB.
|
/// [LocalAssetHashEntity] by local id. Only missing entries are newly hashed and added to the DB.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
import 'package:immich_mobile/extensions/platform_extensions.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/local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
@@ -18,6 +19,7 @@ import 'package:logging/logging.dart';
|
|||||||
|
|
||||||
class LocalSyncService {
|
class LocalSyncService {
|
||||||
final DriftLocalAlbumRepository _localAlbumRepository;
|
final DriftLocalAlbumRepository _localAlbumRepository;
|
||||||
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final NativeSyncApi _nativeSyncApi;
|
final NativeSyncApi _nativeSyncApi;
|
||||||
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
final DriftTrashedLocalAssetRepository _trashedLocalAssetRepository;
|
||||||
final LocalFilesManagerRepository _localFilesManager;
|
final LocalFilesManagerRepository _localFilesManager;
|
||||||
@@ -26,11 +28,13 @@ class LocalSyncService {
|
|||||||
|
|
||||||
LocalSyncService({
|
LocalSyncService({
|
||||||
required DriftLocalAlbumRepository localAlbumRepository,
|
required DriftLocalAlbumRepository localAlbumRepository,
|
||||||
|
required DriftLocalAssetRepository localAssetRepository,
|
||||||
required DriftTrashedLocalAssetRepository trashedLocalAssetRepository,
|
required DriftTrashedLocalAssetRepository trashedLocalAssetRepository,
|
||||||
required LocalFilesManagerRepository localFilesManager,
|
required LocalFilesManagerRepository localFilesManager,
|
||||||
required StorageRepository storageRepository,
|
required StorageRepository storageRepository,
|
||||||
required NativeSyncApi nativeSyncApi,
|
required NativeSyncApi nativeSyncApi,
|
||||||
}) : _localAlbumRepository = localAlbumRepository,
|
}) : _localAlbumRepository = localAlbumRepository,
|
||||||
|
_localAssetRepository = localAssetRepository,
|
||||||
_trashedLocalAssetRepository = trashedLocalAssetRepository,
|
_trashedLocalAssetRepository = trashedLocalAssetRepository,
|
||||||
_localFilesManager = localFilesManager,
|
_localFilesManager = localFilesManager,
|
||||||
_storageRepository = storageRepository,
|
_storageRepository = storageRepository,
|
||||||
@@ -47,6 +51,12 @@ class LocalSyncService {
|
|||||||
_log.warning("syncTrashedAssets cannot proceed because MANAGE_MEDIA permission is missing");
|
_log.warning("syncTrashedAssets cannot proceed because MANAGE_MEDIA permission is missing");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CurrentPlatform.isIOS) {
|
||||||
|
final assets = await _localAssetRepository.getEmptyCloudIdAssets();
|
||||||
|
await _mapIosCloudIds(assets);
|
||||||
|
}
|
||||||
|
|
||||||
if (full || await _nativeSyncApi.shouldFullSync()) {
|
if (full || await _nativeSyncApi.shouldFullSync()) {
|
||||||
_log.fine("Full sync request from ${full ? "user" : "native"}");
|
_log.fine("Full sync request from ${full ? "user" : "native"}");
|
||||||
return await fullSync();
|
return await fullSync();
|
||||||
@@ -63,8 +73,9 @@ class LocalSyncService {
|
|||||||
|
|
||||||
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
final deviceAlbums = await _nativeSyncApi.getAlbums();
|
||||||
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
await _localAlbumRepository.updateAll(deviceAlbums.toLocalAlbums());
|
||||||
|
final newAssets = delta.updates.toLocalAssets();
|
||||||
await _localAlbumRepository.processDelta(
|
await _localAlbumRepository.processDelta(
|
||||||
updates: delta.updates.toLocalAssets(),
|
updates: newAssets,
|
||||||
deletes: delta.deletes,
|
deletes: delta.deletes,
|
||||||
assetAlbums: delta.assetAlbums,
|
assetAlbums: delta.assetAlbums,
|
||||||
);
|
);
|
||||||
@@ -92,6 +103,8 @@ class LocalSyncService {
|
|||||||
}
|
}
|
||||||
await updateAlbum(dbAlbum, album);
|
await updateAlbum(dbAlbum, album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _mapIosCloudIds(newAssets);
|
||||||
}
|
}
|
||||||
await _nativeSyncApi.checkpointSync();
|
await _nativeSyncApi.checkpointSync();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
@@ -130,9 +143,12 @@ class LocalSyncService {
|
|||||||
try {
|
try {
|
||||||
_log.fine("Adding device album ${album.name}");
|
_log.fine("Adding device album ${album.name}");
|
||||||
|
|
||||||
final assets = album.assetCount > 0 ? await _nativeSyncApi.getAssetsForAlbum(album.id) : <PlatformAsset>[];
|
final assets = album.assetCount > 0
|
||||||
|
? await _nativeSyncApi.getAssetsForAlbum(album.id).then((a) => a.toLocalAssets())
|
||||||
|
: <LocalAsset>[];
|
||||||
|
|
||||||
await _localAlbumRepository.upsert(album, toUpsert: assets.toLocalAssets());
|
await _localAlbumRepository.upsert(album, toUpsert: assets);
|
||||||
|
await _mapIosCloudIds(assets);
|
||||||
_log.fine("Successfully added device album ${album.name}");
|
_log.fine("Successfully added device album ${album.name}");
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_log.warning("Error while adding device album", e, s);
|
_log.warning("Error while adding device album", e, s);
|
||||||
@@ -202,13 +218,16 @@ class LocalSyncService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final newAssets = await _nativeSyncApi.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime);
|
final newAssets = await _nativeSyncApi
|
||||||
|
.getAssetsForAlbum(deviceAlbum.id, updatedTimeCond: updatedTime)
|
||||||
|
.then((a) => a.toLocalAssets());
|
||||||
|
|
||||||
await _localAlbumRepository.upsert(
|
await _localAlbumRepository.upsert(
|
||||||
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
deviceAlbum.copyWith(backupSelection: dbAlbum.backupSelection),
|
||||||
toUpsert: newAssets.toLocalAssets(),
|
toUpsert: newAssets,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await _mapIosCloudIds(newAssets);
|
||||||
return true;
|
return true;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_log.warning("Error on fast syncing local album: ${dbAlbum.name}", e, s);
|
_log.warning("Error on fast syncing local album: ${dbAlbum.name}", e, s);
|
||||||
@@ -240,6 +259,7 @@ class LocalSyncService {
|
|||||||
if (dbAlbum.assetCount == 0) {
|
if (dbAlbum.assetCount == 0) {
|
||||||
_log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
|
_log.fine("Device album ${deviceAlbum.name} is empty. Adding assets to DB.");
|
||||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
|
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsInDevice);
|
||||||
|
await _mapIosCloudIds(assetsInDevice);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,6 +297,7 @@ class LocalSyncService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
|
await _localAlbumRepository.upsert(updatedDeviceAlbum, toUpsert: assetsToUpsert, toDelete: assetsToDelete);
|
||||||
|
await _mapIosCloudIds(assetsToUpsert);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
@@ -285,6 +306,16 @@ class LocalSyncService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _mapIosCloudIds(List<LocalAsset> assets) async {
|
||||||
|
if (!CurrentPlatform.isIOS || assets.isEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetIds = assets.map((a) => a.id).toList();
|
||||||
|
final cloudMapping = await _nativeSyncApi.getCloudIdForAssetIds(assetIds);
|
||||||
|
await _localAlbumRepository.updateCloudMapping(cloudMapping);
|
||||||
|
}
|
||||||
|
|
||||||
bool _assetsEqual(LocalAsset a, LocalAsset b) {
|
bool _assetsEqual(LocalAsset a, LocalAsset b) {
|
||||||
if (CurrentPlatform.isAndroid) {
|
if (CurrentPlatform.isAndroid) {
|
||||||
return a.updatedAt.isAtSameMomentAs(b.updatedAt) &&
|
return a.updatedAt.isAtSameMomentAs(b.updatedAt) &&
|
||||||
|
|||||||
@@ -118,6 +118,10 @@ class SyncStreamService {
|
|||||||
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());
|
||||||
|
case SyncEntityType.assetMetadataV1:
|
||||||
|
return _syncStreamRepository.updateAssetsMetadataV1(data.cast());
|
||||||
|
case SyncEntityType.assetMetadataDeleteV1:
|
||||||
|
return _syncStreamRepository.deleteAssetsMetadataV1(data.cast());
|
||||||
case SyncEntityType.partnerAssetV1:
|
case SyncEntityType.partnerAssetV1:
|
||||||
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
|
return _syncStreamRepository.updateAssetsV1(data.cast(), debugLabel: 'partner');
|
||||||
case SyncEntityType.partnerAssetBackfillV1:
|
case SyncEntityType.partnerAssetBackfillV1:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:immich_mobile/domain/utils/migrate_cloud_ids.dart' as m;
|
||||||
import 'package:immich_mobile/domain/utils/sync_linked_album.dart';
|
import 'package:immich_mobile/domain/utils/sync_linked_album.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||||
import 'package:immich_mobile/utils/isolate.dart';
|
import 'package:immich_mobile/utils/isolate.dart';
|
||||||
@@ -22,8 +23,13 @@ class BackgroundSyncManager {
|
|||||||
final SyncCallback? onHashingComplete;
|
final SyncCallback? onHashingComplete;
|
||||||
final SyncErrorCallback? onHashingError;
|
final SyncErrorCallback? onHashingError;
|
||||||
|
|
||||||
|
final SyncCallback? onCloudIdSyncStart;
|
||||||
|
final SyncCallback? onCloudIdSyncComplete;
|
||||||
|
final SyncErrorCallback? onCloudIdSyncError;
|
||||||
|
|
||||||
Cancelable<bool?>? _syncTask;
|
Cancelable<bool?>? _syncTask;
|
||||||
Cancelable<void>? _syncWebsocketTask;
|
Cancelable<void>? _syncWebsocketTask;
|
||||||
|
Cancelable<void>? _cloudIdSyncTask;
|
||||||
Cancelable<void>? _deviceAlbumSyncTask;
|
Cancelable<void>? _deviceAlbumSyncTask;
|
||||||
Cancelable<void>? _linkedAlbumSyncTask;
|
Cancelable<void>? _linkedAlbumSyncTask;
|
||||||
Cancelable<void>? _hashTask;
|
Cancelable<void>? _hashTask;
|
||||||
@@ -38,6 +44,9 @@ class BackgroundSyncManager {
|
|||||||
this.onHashingStart,
|
this.onHashingStart,
|
||||||
this.onHashingComplete,
|
this.onHashingComplete,
|
||||||
this.onHashingError,
|
this.onHashingError,
|
||||||
|
this.onCloudIdSyncStart,
|
||||||
|
this.onCloudIdSyncComplete,
|
||||||
|
this.onCloudIdSyncError,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> cancel() async {
|
Future<void> cancel() async {
|
||||||
@@ -55,6 +64,12 @@ class BackgroundSyncManager {
|
|||||||
_syncWebsocketTask?.cancel();
|
_syncWebsocketTask?.cancel();
|
||||||
_syncWebsocketTask = null;
|
_syncWebsocketTask = null;
|
||||||
|
|
||||||
|
if (_cloudIdSyncTask != null) {
|
||||||
|
futures.add(_cloudIdSyncTask!.future);
|
||||||
|
}
|
||||||
|
_cloudIdSyncTask?.cancel();
|
||||||
|
_cloudIdSyncTask = null;
|
||||||
|
|
||||||
if (_linkedAlbumSyncTask != null) {
|
if (_linkedAlbumSyncTask != null) {
|
||||||
futures.add(_linkedAlbumSyncTask!.future);
|
futures.add(_linkedAlbumSyncTask!.future);
|
||||||
}
|
}
|
||||||
@@ -121,7 +136,6 @@ class BackgroundSyncManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to cancel the task, as it can also be run when the user logs out
|
|
||||||
Future<void> hashAssets() {
|
Future<void> hashAssets() {
|
||||||
if (_hashTask != null) {
|
if (_hashTask != null) {
|
||||||
return _hashTask!.future;
|
return _hashTask!.future;
|
||||||
@@ -192,6 +206,25 @@ class BackgroundSyncManager {
|
|||||||
_linkedAlbumSyncTask = null;
|
_linkedAlbumSyncTask = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> syncCloudIds() {
|
||||||
|
if (_cloudIdSyncTask != null) {
|
||||||
|
return _cloudIdSyncTask!.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloudIdSyncStart?.call();
|
||||||
|
|
||||||
|
_cloudIdSyncTask = runInIsolateGentle(computation: m.syncCloudIds);
|
||||||
|
return _cloudIdSyncTask!
|
||||||
|
.whenComplete(() {
|
||||||
|
onCloudIdSyncComplete?.call();
|
||||||
|
_cloudIdSyncTask = null;
|
||||||
|
})
|
||||||
|
.catchError((error) {
|
||||||
|
onCloudIdSyncError?.call(error.toString());
|
||||||
|
_cloudIdSyncTask = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
||||||
|
|||||||
93
mobile/lib/domain/utils/migrate_cloud_ids.dart
Normal file
93
mobile/lib/domain/utils/migrate_cloud_ids.dart
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
||||||
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/sync.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
// ignore: import_rule_openapi
|
||||||
|
import 'package:openapi/api.dart';
|
||||||
|
|
||||||
|
Future<void> syncCloudIds(ProviderContainer ref) async {
|
||||||
|
final db = ref.read(driftProvider);
|
||||||
|
// Populate cloud IDs for local assets that don't have one yet
|
||||||
|
await _populateCloudIds(db);
|
||||||
|
|
||||||
|
// Wait for remote sync to complete, so we have up-to-date asset metadata entries
|
||||||
|
await ref.read(syncStreamServiceProvider).sync();
|
||||||
|
|
||||||
|
// Fetch the mapping for backed up assets that have a cloud ID locally but do not have a cloud ID on the server
|
||||||
|
final currentUser = ref.read(currentUserProvider);
|
||||||
|
if (currentUser == null) {
|
||||||
|
Logger('migrateCloudIds').warning('Current user is null. Aborting cloudId migration.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final mappingsToUpdate = await _fetchCloudIdMappings(db, currentUser.id);
|
||||||
|
final assetApi = ref.read(apiServiceProvider).assetsApi;
|
||||||
|
for (final mapping in mappingsToUpdate) {
|
||||||
|
final mobileMeta = AssetMetadataUpsertItemDto(
|
||||||
|
key: AssetMetadataKey.mobileApp,
|
||||||
|
value: RemoteAssetMobileAppMetadata(
|
||||||
|
cloudId: mapping.localAsset.cloudId,
|
||||||
|
createdAt: mapping.localAsset.createdAt.toIso8601String(),
|
||||||
|
adjustmentTime: mapping.localAsset.adjustmentTime?.toIso8601String(),
|
||||||
|
latitude: mapping.localAsset.latitude?.toString(),
|
||||||
|
longitude: mapping.localAsset.longitude?.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await assetApi.updateAssetMetadata(mapping.remoteAssetId, AssetMetadataUpsertDto(items: [mobileMeta]));
|
||||||
|
} catch (error, stack) {
|
||||||
|
Logger('migrateCloudIds').warning('Failed to update metadata for asset ${mapping.remoteAssetId}', error, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _populateCloudIds(Drift drift) async {
|
||||||
|
final query = drift.localAssetEntity.selectOnly()
|
||||||
|
..addColumns([drift.localAssetEntity.id])
|
||||||
|
..where(drift.localAssetEntity.iCloudId.isNull());
|
||||||
|
final ids = await query.map((row) => row.read(drift.localAssetEntity.id)!).get();
|
||||||
|
final cloudMapping = await NativeSyncApi().getCloudIdForAssetIds(ids);
|
||||||
|
await DriftLocalAlbumRepository(drift).updateCloudMapping(cloudMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef _CloudIdMapping = ({String remoteAssetId, LocalAsset localAsset});
|
||||||
|
|
||||||
|
Future<List<_CloudIdMapping>> _fetchCloudIdMappings(Drift drift, String userId) async {
|
||||||
|
final query =
|
||||||
|
drift.remoteAssetEntity.select().join([
|
||||||
|
leftOuterJoin(
|
||||||
|
drift.localAssetEntity,
|
||||||
|
drift.localAssetEntity.checksum.equalsExp(drift.remoteAssetEntity.checksum),
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
drift.remoteAssetCloudIdEntity,
|
||||||
|
drift.remoteAssetEntity.id.equalsExp(drift.remoteAssetCloudIdEntity.assetId),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])..where(
|
||||||
|
// Only select assets that have a local cloud ID but either no remote cloud ID or a mismatched eTag
|
||||||
|
drift.localAssetEntity.id.isNotNull() &
|
||||||
|
drift.localAssetEntity.iCloudId.isNotNull() &
|
||||||
|
drift.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
(drift.remoteAssetCloudIdEntity.cloudId.isNull() |
|
||||||
|
((drift.remoteAssetCloudIdEntity.adjustmentTime.isNotExp(drift.localAssetEntity.adjustmentTime)) &
|
||||||
|
(drift.remoteAssetCloudIdEntity.latitude.isNotExp(drift.localAssetEntity.latitude)) &
|
||||||
|
(drift.remoteAssetCloudIdEntity.longitude.isNotExp(drift.localAssetEntity.longitude)) &
|
||||||
|
(drift.remoteAssetCloudIdEntity.createdAt.isNotExp(drift.localAssetEntity.createdAt)))),
|
||||||
|
);
|
||||||
|
return query.map((row) {
|
||||||
|
return (
|
||||||
|
remoteAssetId: row.read(drift.remoteAssetEntity.id)!,
|
||||||
|
localAsset: row.readTable(drift.localAssetEntity).toDto(),
|
||||||
|
);
|
||||||
|
}).get();
|
||||||
|
}
|
||||||
8
mobile/lib/extensions/num_extensions.dart
Normal file
8
mobile/lib/extensions/num_extensions.dart
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
extension DoubleTruncate on double {
|
||||||
|
double truncateTo(int fractionDigits) {
|
||||||
|
final mod = math.pow(10.0, fractionDigits);
|
||||||
|
return ((this * mod).truncate() / mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import 'package:immich_mobile/infrastructure/utils/asset.mixin.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_local_asset_checksum ON local_asset_entity (checksum)')
|
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)')
|
||||||
|
@TableIndex.sql('CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)')
|
||||||
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
||||||
const LocalAssetEntity();
|
const LocalAssetEntity();
|
||||||
|
|
||||||
@@ -16,6 +17,8 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
|||||||
|
|
||||||
IntColumn get orientation => integer().withDefault(const Constant(0))();
|
IntColumn get orientation => integer().withDefault(const Constant(0))();
|
||||||
|
|
||||||
|
TextColumn get iCloudId => text().nullable()();
|
||||||
|
|
||||||
DateTimeColumn get adjustmentTime => dateTime().nullable()();
|
DateTimeColumn get adjustmentTime => dateTime().nullable()();
|
||||||
|
|
||||||
RealColumn get latitude => real().nullable()();
|
RealColumn get latitude => real().nullable()();
|
||||||
@@ -43,5 +46,6 @@ extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData {
|
|||||||
adjustmentTime: adjustmentTime,
|
adjustmentTime: adjustmentTime,
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
|
cloudId: iCloudId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ typedef $$LocalAssetEntityTableCreateCompanionBuilder =
|
|||||||
i0.Value<String?> checksum,
|
i0.Value<String?> checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
i0.Value<int> orientation,
|
i0.Value<int> orientation,
|
||||||
|
i0.Value<String?> iCloudId,
|
||||||
i0.Value<DateTime?> adjustmentTime,
|
i0.Value<DateTime?> adjustmentTime,
|
||||||
i0.Value<double?> latitude,
|
i0.Value<double?> latitude,
|
||||||
i0.Value<double?> longitude,
|
i0.Value<double?> longitude,
|
||||||
@@ -38,6 +39,7 @@ typedef $$LocalAssetEntityTableUpdateCompanionBuilder =
|
|||||||
i0.Value<String?> checksum,
|
i0.Value<String?> checksum,
|
||||||
i0.Value<bool> isFavorite,
|
i0.Value<bool> isFavorite,
|
||||||
i0.Value<int> orientation,
|
i0.Value<int> orientation,
|
||||||
|
i0.Value<String?> iCloudId,
|
||||||
i0.Value<DateTime?> adjustmentTime,
|
i0.Value<DateTime?> adjustmentTime,
|
||||||
i0.Value<double?> latitude,
|
i0.Value<double?> latitude,
|
||||||
i0.Value<double?> longitude,
|
i0.Value<double?> longitude,
|
||||||
@@ -108,6 +110,11 @@ class $$LocalAssetEntityTableFilterComposer
|
|||||||
builder: (column) => i0.ColumnFilters(column),
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
i0.ColumnFilters<String> get iCloudId => $composableBuilder(
|
||||||
|
column: $table.iCloudId,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
i0.ColumnFilters<DateTime> get adjustmentTime => $composableBuilder(
|
i0.ColumnFilters<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
column: $table.adjustmentTime,
|
column: $table.adjustmentTime,
|
||||||
builder: (column) => i0.ColumnFilters(column),
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
@@ -188,6 +195,11 @@ class $$LocalAssetEntityTableOrderingComposer
|
|||||||
builder: (column) => i0.ColumnOrderings(column),
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
i0.ColumnOrderings<String> get iCloudId => $composableBuilder(
|
||||||
|
column: $table.iCloudId,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
i0.ColumnOrderings<DateTime> get adjustmentTime => $composableBuilder(
|
i0.ColumnOrderings<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
column: $table.adjustmentTime,
|
column: $table.adjustmentTime,
|
||||||
builder: (column) => i0.ColumnOrderings(column),
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
@@ -252,6 +264,9 @@ class $$LocalAssetEntityTableAnnotationComposer
|
|||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<String> get iCloudId =>
|
||||||
|
$composableBuilder(column: $table.iCloudId, builder: (column) => column);
|
||||||
|
|
||||||
i0.GeneratedColumn<DateTime> get adjustmentTime => $composableBuilder(
|
i0.GeneratedColumn<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
column: $table.adjustmentTime,
|
column: $table.adjustmentTime,
|
||||||
builder: (column) => column,
|
builder: (column) => column,
|
||||||
@@ -315,6 +330,7 @@ class $$LocalAssetEntityTableTableManager
|
|||||||
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(),
|
||||||
i0.Value<int> orientation = const i0.Value.absent(),
|
i0.Value<int> orientation = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> iCloudId = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
i0.Value<double?> latitude = const i0.Value.absent(),
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
i0.Value<double?> longitude = const i0.Value.absent(),
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
@@ -330,6 +346,7 @@ class $$LocalAssetEntityTableTableManager
|
|||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
orientation: orientation,
|
orientation: orientation,
|
||||||
|
iCloudId: iCloudId,
|
||||||
adjustmentTime: adjustmentTime,
|
adjustmentTime: adjustmentTime,
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
@@ -347,6 +364,7 @@ class $$LocalAssetEntityTableTableManager
|
|||||||
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(),
|
||||||
i0.Value<int> orientation = const i0.Value.absent(),
|
i0.Value<int> orientation = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> iCloudId = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
i0.Value<double?> latitude = const i0.Value.absent(),
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
i0.Value<double?> longitude = const i0.Value.absent(),
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
@@ -362,6 +380,7 @@ class $$LocalAssetEntityTableTableManager
|
|||||||
checksum: checksum,
|
checksum: checksum,
|
||||||
isFavorite: isFavorite,
|
isFavorite: isFavorite,
|
||||||
orientation: orientation,
|
orientation: orientation,
|
||||||
|
iCloudId: iCloudId,
|
||||||
adjustmentTime: adjustmentTime,
|
adjustmentTime: adjustmentTime,
|
||||||
latitude: latitude,
|
latitude: latitude,
|
||||||
longitude: longitude,
|
longitude: longitude,
|
||||||
@@ -532,6 +551,17 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
requiredDuringInsert: false,
|
requiredDuringInsert: false,
|
||||||
defaultValue: const i4.Constant(0),
|
defaultValue: const i4.Constant(0),
|
||||||
);
|
);
|
||||||
|
static const i0.VerificationMeta _iCloudIdMeta = const i0.VerificationMeta(
|
||||||
|
'iCloudId',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> iCloudId = i0.GeneratedColumn<String>(
|
||||||
|
'i_cloud_id',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
static const i0.VerificationMeta _adjustmentTimeMeta =
|
static const i0.VerificationMeta _adjustmentTimeMeta =
|
||||||
const i0.VerificationMeta('adjustmentTime');
|
const i0.VerificationMeta('adjustmentTime');
|
||||||
@override
|
@override
|
||||||
@@ -578,6 +608,7 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
checksum,
|
checksum,
|
||||||
isFavorite,
|
isFavorite,
|
||||||
orientation,
|
orientation,
|
||||||
|
iCloudId,
|
||||||
adjustmentTime,
|
adjustmentTime,
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
@@ -661,6 +692,12 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (data.containsKey('i_cloud_id')) {
|
||||||
|
context.handle(
|
||||||
|
_iCloudIdMeta,
|
||||||
|
iCloudId.isAcceptableOrUnknown(data['i_cloud_id']!, _iCloudIdMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data.containsKey('adjustment_time')) {
|
if (data.containsKey('adjustment_time')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_adjustmentTimeMeta,
|
_adjustmentTimeMeta,
|
||||||
@@ -740,6 +777,10 @@ class $LocalAssetEntityTable extends i3.LocalAssetEntity
|
|||||||
i0.DriftSqlType.int,
|
i0.DriftSqlType.int,
|
||||||
data['${effectivePrefix}orientation'],
|
data['${effectivePrefix}orientation'],
|
||||||
)!,
|
)!,
|
||||||
|
iCloudId: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}i_cloud_id'],
|
||||||
|
),
|
||||||
adjustmentTime: attachedDatabase.typeMapping.read(
|
adjustmentTime: attachedDatabase.typeMapping.read(
|
||||||
i0.DriftSqlType.dateTime,
|
i0.DriftSqlType.dateTime,
|
||||||
data['${effectivePrefix}adjustment_time'],
|
data['${effectivePrefix}adjustment_time'],
|
||||||
@@ -781,6 +822,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
final String? checksum;
|
final String? checksum;
|
||||||
final bool isFavorite;
|
final bool isFavorite;
|
||||||
final int orientation;
|
final int orientation;
|
||||||
|
final String? iCloudId;
|
||||||
final DateTime? adjustmentTime;
|
final DateTime? adjustmentTime;
|
||||||
final double? latitude;
|
final double? latitude;
|
||||||
final double? longitude;
|
final double? longitude;
|
||||||
@@ -796,6 +838,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
this.checksum,
|
this.checksum,
|
||||||
required this.isFavorite,
|
required this.isFavorite,
|
||||||
required this.orientation,
|
required this.orientation,
|
||||||
|
this.iCloudId,
|
||||||
this.adjustmentTime,
|
this.adjustmentTime,
|
||||||
this.latitude,
|
this.latitude,
|
||||||
this.longitude,
|
this.longitude,
|
||||||
@@ -826,6 +869,9 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
}
|
}
|
||||||
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
map['is_favorite'] = i0.Variable<bool>(isFavorite);
|
||||||
map['orientation'] = i0.Variable<int>(orientation);
|
map['orientation'] = i0.Variable<int>(orientation);
|
||||||
|
if (!nullToAbsent || iCloudId != null) {
|
||||||
|
map['i_cloud_id'] = i0.Variable<String>(iCloudId);
|
||||||
|
}
|
||||||
if (!nullToAbsent || adjustmentTime != null) {
|
if (!nullToAbsent || adjustmentTime != null) {
|
||||||
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime);
|
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime);
|
||||||
}
|
}
|
||||||
@@ -857,6 +903,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
checksum: serializer.fromJson<String?>(json['checksum']),
|
checksum: serializer.fromJson<String?>(json['checksum']),
|
||||||
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
isFavorite: serializer.fromJson<bool>(json['isFavorite']),
|
||||||
orientation: serializer.fromJson<int>(json['orientation']),
|
orientation: serializer.fromJson<int>(json['orientation']),
|
||||||
|
iCloudId: serializer.fromJson<String?>(json['iCloudId']),
|
||||||
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
|
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
|
||||||
latitude: serializer.fromJson<double?>(json['latitude']),
|
latitude: serializer.fromJson<double?>(json['latitude']),
|
||||||
longitude: serializer.fromJson<double?>(json['longitude']),
|
longitude: serializer.fromJson<double?>(json['longitude']),
|
||||||
@@ -879,6 +926,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
'checksum': serializer.toJson<String?>(checksum),
|
'checksum': serializer.toJson<String?>(checksum),
|
||||||
'isFavorite': serializer.toJson<bool>(isFavorite),
|
'isFavorite': serializer.toJson<bool>(isFavorite),
|
||||||
'orientation': serializer.toJson<int>(orientation),
|
'orientation': serializer.toJson<int>(orientation),
|
||||||
|
'iCloudId': serializer.toJson<String?>(iCloudId),
|
||||||
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
|
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
|
||||||
'latitude': serializer.toJson<double?>(latitude),
|
'latitude': serializer.toJson<double?>(latitude),
|
||||||
'longitude': serializer.toJson<double?>(longitude),
|
'longitude': serializer.toJson<double?>(longitude),
|
||||||
@@ -897,6 +945,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
i0.Value<String?> checksum = const i0.Value.absent(),
|
i0.Value<String?> checksum = const i0.Value.absent(),
|
||||||
bool? isFavorite,
|
bool? isFavorite,
|
||||||
int? orientation,
|
int? orientation,
|
||||||
|
i0.Value<String?> iCloudId = const i0.Value.absent(),
|
||||||
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
i0.Value<double?> latitude = const i0.Value.absent(),
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
i0.Value<double?> longitude = const i0.Value.absent(),
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
@@ -914,6 +963,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
checksum: checksum.present ? checksum.value : this.checksum,
|
checksum: checksum.present ? checksum.value : this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
orientation: orientation ?? this.orientation,
|
orientation: orientation ?? this.orientation,
|
||||||
|
iCloudId: iCloudId.present ? iCloudId.value : this.iCloudId,
|
||||||
adjustmentTime: adjustmentTime.present
|
adjustmentTime: adjustmentTime.present
|
||||||
? adjustmentTime.value
|
? adjustmentTime.value
|
||||||
: this.adjustmentTime,
|
: this.adjustmentTime,
|
||||||
@@ -939,6 +989,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
orientation: data.orientation.present
|
orientation: data.orientation.present
|
||||||
? data.orientation.value
|
? data.orientation.value
|
||||||
: this.orientation,
|
: this.orientation,
|
||||||
|
iCloudId: data.iCloudId.present ? data.iCloudId.value : this.iCloudId,
|
||||||
adjustmentTime: data.adjustmentTime.present
|
adjustmentTime: data.adjustmentTime.present
|
||||||
? data.adjustmentTime.value
|
? data.adjustmentTime.value
|
||||||
: this.adjustmentTime,
|
: this.adjustmentTime,
|
||||||
@@ -961,6 +1012,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite, ')
|
..write('isFavorite: $isFavorite, ')
|
||||||
..write('orientation: $orientation, ')
|
..write('orientation: $orientation, ')
|
||||||
|
..write('iCloudId: $iCloudId, ')
|
||||||
..write('adjustmentTime: $adjustmentTime, ')
|
..write('adjustmentTime: $adjustmentTime, ')
|
||||||
..write('latitude: $latitude, ')
|
..write('latitude: $latitude, ')
|
||||||
..write('longitude: $longitude')
|
..write('longitude: $longitude')
|
||||||
@@ -981,6 +1033,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
checksum,
|
checksum,
|
||||||
isFavorite,
|
isFavorite,
|
||||||
orientation,
|
orientation,
|
||||||
|
iCloudId,
|
||||||
adjustmentTime,
|
adjustmentTime,
|
||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
@@ -1000,6 +1053,7 @@ class LocalAssetEntityData extends i0.DataClass
|
|||||||
other.checksum == this.checksum &&
|
other.checksum == this.checksum &&
|
||||||
other.isFavorite == this.isFavorite &&
|
other.isFavorite == this.isFavorite &&
|
||||||
other.orientation == this.orientation &&
|
other.orientation == this.orientation &&
|
||||||
|
other.iCloudId == this.iCloudId &&
|
||||||
other.adjustmentTime == this.adjustmentTime &&
|
other.adjustmentTime == this.adjustmentTime &&
|
||||||
other.latitude == this.latitude &&
|
other.latitude == this.latitude &&
|
||||||
other.longitude == this.longitude);
|
other.longitude == this.longitude);
|
||||||
@@ -1018,6 +1072,7 @@ class LocalAssetEntityCompanion
|
|||||||
final i0.Value<String?> checksum;
|
final i0.Value<String?> checksum;
|
||||||
final i0.Value<bool> isFavorite;
|
final i0.Value<bool> isFavorite;
|
||||||
final i0.Value<int> orientation;
|
final i0.Value<int> orientation;
|
||||||
|
final i0.Value<String?> iCloudId;
|
||||||
final i0.Value<DateTime?> adjustmentTime;
|
final i0.Value<DateTime?> adjustmentTime;
|
||||||
final i0.Value<double?> latitude;
|
final i0.Value<double?> latitude;
|
||||||
final i0.Value<double?> longitude;
|
final i0.Value<double?> longitude;
|
||||||
@@ -1033,6 +1088,7 @@ class LocalAssetEntityCompanion
|
|||||||
this.checksum = const i0.Value.absent(),
|
this.checksum = const i0.Value.absent(),
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
this.orientation = const i0.Value.absent(),
|
this.orientation = const i0.Value.absent(),
|
||||||
|
this.iCloudId = const i0.Value.absent(),
|
||||||
this.adjustmentTime = const i0.Value.absent(),
|
this.adjustmentTime = const i0.Value.absent(),
|
||||||
this.latitude = const i0.Value.absent(),
|
this.latitude = const i0.Value.absent(),
|
||||||
this.longitude = const i0.Value.absent(),
|
this.longitude = const i0.Value.absent(),
|
||||||
@@ -1049,6 +1105,7 @@ class LocalAssetEntityCompanion
|
|||||||
this.checksum = const i0.Value.absent(),
|
this.checksum = const i0.Value.absent(),
|
||||||
this.isFavorite = const i0.Value.absent(),
|
this.isFavorite = const i0.Value.absent(),
|
||||||
this.orientation = const i0.Value.absent(),
|
this.orientation = const i0.Value.absent(),
|
||||||
|
this.iCloudId = const i0.Value.absent(),
|
||||||
this.adjustmentTime = const i0.Value.absent(),
|
this.adjustmentTime = const i0.Value.absent(),
|
||||||
this.latitude = const i0.Value.absent(),
|
this.latitude = const i0.Value.absent(),
|
||||||
this.longitude = const i0.Value.absent(),
|
this.longitude = const i0.Value.absent(),
|
||||||
@@ -1067,6 +1124,7 @@ class LocalAssetEntityCompanion
|
|||||||
i0.Expression<String>? checksum,
|
i0.Expression<String>? checksum,
|
||||||
i0.Expression<bool>? isFavorite,
|
i0.Expression<bool>? isFavorite,
|
||||||
i0.Expression<int>? orientation,
|
i0.Expression<int>? orientation,
|
||||||
|
i0.Expression<String>? iCloudId,
|
||||||
i0.Expression<DateTime>? adjustmentTime,
|
i0.Expression<DateTime>? adjustmentTime,
|
||||||
i0.Expression<double>? latitude,
|
i0.Expression<double>? latitude,
|
||||||
i0.Expression<double>? longitude,
|
i0.Expression<double>? longitude,
|
||||||
@@ -1083,6 +1141,7 @@ class LocalAssetEntityCompanion
|
|||||||
if (checksum != null) 'checksum': checksum,
|
if (checksum != null) 'checksum': checksum,
|
||||||
if (isFavorite != null) 'is_favorite': isFavorite,
|
if (isFavorite != null) 'is_favorite': isFavorite,
|
||||||
if (orientation != null) 'orientation': orientation,
|
if (orientation != null) 'orientation': orientation,
|
||||||
|
if (iCloudId != null) 'i_cloud_id': iCloudId,
|
||||||
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
|
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
|
||||||
if (latitude != null) 'latitude': latitude,
|
if (latitude != null) 'latitude': latitude,
|
||||||
if (longitude != null) 'longitude': longitude,
|
if (longitude != null) 'longitude': longitude,
|
||||||
@@ -1101,6 +1160,7 @@ class LocalAssetEntityCompanion
|
|||||||
i0.Value<String?>? checksum,
|
i0.Value<String?>? checksum,
|
||||||
i0.Value<bool>? isFavorite,
|
i0.Value<bool>? isFavorite,
|
||||||
i0.Value<int>? orientation,
|
i0.Value<int>? orientation,
|
||||||
|
i0.Value<String?>? iCloudId,
|
||||||
i0.Value<DateTime?>? adjustmentTime,
|
i0.Value<DateTime?>? adjustmentTime,
|
||||||
i0.Value<double?>? latitude,
|
i0.Value<double?>? latitude,
|
||||||
i0.Value<double?>? longitude,
|
i0.Value<double?>? longitude,
|
||||||
@@ -1117,6 +1177,7 @@ class LocalAssetEntityCompanion
|
|||||||
checksum: checksum ?? this.checksum,
|
checksum: checksum ?? this.checksum,
|
||||||
isFavorite: isFavorite ?? this.isFavorite,
|
isFavorite: isFavorite ?? this.isFavorite,
|
||||||
orientation: orientation ?? this.orientation,
|
orientation: orientation ?? this.orientation,
|
||||||
|
iCloudId: iCloudId ?? this.iCloudId,
|
||||||
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
|
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
|
||||||
latitude: latitude ?? this.latitude,
|
latitude: latitude ?? this.latitude,
|
||||||
longitude: longitude ?? this.longitude,
|
longitude: longitude ?? this.longitude,
|
||||||
@@ -1161,6 +1222,9 @@ class LocalAssetEntityCompanion
|
|||||||
if (orientation.present) {
|
if (orientation.present) {
|
||||||
map['orientation'] = i0.Variable<int>(orientation.value);
|
map['orientation'] = i0.Variable<int>(orientation.value);
|
||||||
}
|
}
|
||||||
|
if (iCloudId.present) {
|
||||||
|
map['i_cloud_id'] = i0.Variable<String>(iCloudId.value);
|
||||||
|
}
|
||||||
if (adjustmentTime.present) {
|
if (adjustmentTime.present) {
|
||||||
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime.value);
|
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime.value);
|
||||||
}
|
}
|
||||||
@@ -1187,6 +1251,7 @@ class LocalAssetEntityCompanion
|
|||||||
..write('checksum: $checksum, ')
|
..write('checksum: $checksum, ')
|
||||||
..write('isFavorite: $isFavorite, ')
|
..write('isFavorite: $isFavorite, ')
|
||||||
..write('orientation: $orientation, ')
|
..write('orientation: $orientation, ')
|
||||||
|
..write('iCloudId: $iCloudId, ')
|
||||||
..write('adjustmentTime: $adjustmentTime, ')
|
..write('adjustmentTime: $adjustmentTime, ')
|
||||||
..write('latitude: $latitude, ')
|
..write('latitude: $latitude, ')
|
||||||
..write('longitude: $longitude')
|
..write('longitude: $longitude')
|
||||||
@@ -1194,3 +1259,8 @@ class LocalAssetEntityCompanion
|
|||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i0.Index get idxLocalAssetCloudId => i0.Index(
|
||||||
|
'idx_local_asset_cloud_id',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)',
|
||||||
|
);
|
||||||
|
|||||||
@@ -21,7 +21,11 @@ SELECT
|
|||||||
rae.owner_id,
|
rae.owner_id,
|
||||||
rae.live_photo_video_id,
|
rae.live_photo_video_id,
|
||||||
0 as orientation,
|
0 as orientation,
|
||||||
rae.stack_id
|
rae.stack_id,
|
||||||
|
NULL as i_cloud_id,
|
||||||
|
NULL as latitude,
|
||||||
|
NULL as longitude,
|
||||||
|
NULL as adjustmentTime
|
||||||
FROM
|
FROM
|
||||||
remote_asset_entity rae
|
remote_asset_entity rae
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
@@ -53,7 +57,11 @@ SELECT
|
|||||||
NULL as owner_id,
|
NULL as owner_id,
|
||||||
NULL as live_photo_video_id,
|
NULL as live_photo_video_id,
|
||||||
lae.orientation,
|
lae.orientation,
|
||||||
NULL as stack_id
|
NULL as stack_id,
|
||||||
|
lae.i_cloud_id,
|
||||||
|
lae.latitude,
|
||||||
|
lae.longitude,
|
||||||
|
lae.adjustment_time
|
||||||
FROM
|
FROM
|
||||||
local_asset_entity lae
|
local_asset_entity lae
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
|
|||||||
@@ -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 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 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_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 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 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,
|
||||||
@@ -62,6 +62,10 @@ class MergedAssetDrift extends i1.ModularAccessor {
|
|||||||
livePhotoVideoId: row.readNullable<String>('live_photo_video_id'),
|
livePhotoVideoId: row.readNullable<String>('live_photo_video_id'),
|
||||||
orientation: row.read<int>('orientation'),
|
orientation: row.read<int>('orientation'),
|
||||||
stackId: row.readNullable<String>('stack_id'),
|
stackId: row.readNullable<String>('stack_id'),
|
||||||
|
iCloudId: row.readNullable<String>('i_cloud_id'),
|
||||||
|
latitude: row.readNullable<double>('latitude'),
|
||||||
|
longitude: row.readNullable<double>('longitude'),
|
||||||
|
adjustmentTime: row.readNullable<DateTime>('adjustmentTime'),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -129,6 +133,10 @@ class MergedAssetResult {
|
|||||||
final String? livePhotoVideoId;
|
final String? livePhotoVideoId;
|
||||||
final int orientation;
|
final int orientation;
|
||||||
final String? stackId;
|
final String? stackId;
|
||||||
|
final String? iCloudId;
|
||||||
|
final double? latitude;
|
||||||
|
final double? longitude;
|
||||||
|
final DateTime? adjustmentTime;
|
||||||
MergedAssetResult({
|
MergedAssetResult({
|
||||||
this.remoteId,
|
this.remoteId,
|
||||||
this.localId,
|
this.localId,
|
||||||
@@ -146,6 +154,10 @@ class MergedAssetResult {
|
|||||||
this.livePhotoVideoId,
|
this.livePhotoVideoId,
|
||||||
required this.orientation,
|
required this.orientation,
|
||||||
this.stackId,
|
this.stackId,
|
||||||
|
this.iCloudId,
|
||||||
|
this.latitude,
|
||||||
|
this.longitude,
|
||||||
|
this.adjustmentTime,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:drift/drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||||
|
|
||||||
|
class RemoteAssetCloudIdEntity extends Table with DriftDefaultsMixin {
|
||||||
|
TextColumn get assetId => text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
|
||||||
|
|
||||||
|
TextColumn get cloudId => text().unique().nullable()();
|
||||||
|
|
||||||
|
DateTimeColumn get createdAt => dateTime().nullable()();
|
||||||
|
|
||||||
|
DateTimeColumn get adjustmentTime => dateTime().nullable()();
|
||||||
|
|
||||||
|
RealColumn get latitude => real().nullable()();
|
||||||
|
|
||||||
|
RealColumn get longitude => real().nullable()();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<Column> get primaryKey => {assetId};
|
||||||
|
}
|
||||||
827
mobile/lib/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart
generated
Normal file
827
mobile/lib/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart
generated
Normal file
@@ -0,0 +1,827 @@
|
|||||||
|
// dart format width=80
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
import 'package:drift/drift.dart' as i0;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart'
|
||||||
|
as i1;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.dart'
|
||||||
|
as i2;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
|
||||||
|
as i3;
|
||||||
|
import 'package:drift/internal/modular.dart' as i4;
|
||||||
|
|
||||||
|
typedef $$RemoteAssetCloudIdEntityTableCreateCompanionBuilder =
|
||||||
|
i1.RemoteAssetCloudIdEntityCompanion Function({
|
||||||
|
required String assetId,
|
||||||
|
i0.Value<String?> cloudId,
|
||||||
|
i0.Value<DateTime?> createdAt,
|
||||||
|
i0.Value<DateTime?> adjustmentTime,
|
||||||
|
i0.Value<double?> latitude,
|
||||||
|
i0.Value<double?> longitude,
|
||||||
|
});
|
||||||
|
typedef $$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder =
|
||||||
|
i1.RemoteAssetCloudIdEntityCompanion Function({
|
||||||
|
i0.Value<String> assetId,
|
||||||
|
i0.Value<String?> cloudId,
|
||||||
|
i0.Value<DateTime?> createdAt,
|
||||||
|
i0.Value<DateTime?> adjustmentTime,
|
||||||
|
i0.Value<double?> latitude,
|
||||||
|
i0.Value<double?> longitude,
|
||||||
|
});
|
||||||
|
|
||||||
|
final class $$RemoteAssetCloudIdEntityTableReferences
|
||||||
|
extends
|
||||||
|
i0.BaseReferences<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$RemoteAssetCloudIdEntityTable,
|
||||||
|
i1.RemoteAssetCloudIdEntityData
|
||||||
|
> {
|
||||||
|
$$RemoteAssetCloudIdEntityTableReferences(
|
||||||
|
super.$_db,
|
||||||
|
super.$_table,
|
||||||
|
super.$_typedResult,
|
||||||
|
);
|
||||||
|
|
||||||
|
static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
|
||||||
|
i4.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
|
||||||
|
.createAlias(
|
||||||
|
i0.$_aliasNameGenerator(
|
||||||
|
i4.ReadDatabaseContainer(db)
|
||||||
|
.resultSet<i1.$RemoteAssetCloudIdEntityTable>(
|
||||||
|
'remote_asset_cloud_id_entity',
|
||||||
|
)
|
||||||
|
.assetId,
|
||||||
|
i4.ReadDatabaseContainer(
|
||||||
|
db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity').id,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
|
||||||
|
final $_column = $_itemColumn<String>('asset_id')!;
|
||||||
|
|
||||||
|
final manager = i3
|
||||||
|
.$$RemoteAssetEntityTableTableManager(
|
||||||
|
$_db,
|
||||||
|
i4.ReadDatabaseContainer(
|
||||||
|
$_db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
)
|
||||||
|
.filter((f) => f.id.sqlEquals($_column));
|
||||||
|
final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
|
||||||
|
if (item == null) return manager;
|
||||||
|
return i0.ProcessedTableManager(
|
||||||
|
manager.$state.copyWith(prefetchedData: [item]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$RemoteAssetCloudIdEntityTableFilterComposer
|
||||||
|
extends
|
||||||
|
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
|
||||||
|
$$RemoteAssetCloudIdEntityTableFilterComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.ColumnFilters<String> get cloudId => $composableBuilder(
|
||||||
|
column: $table.cloudId,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
|
||||||
|
column: $table.createdAt,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnFilters<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
|
column: $table.adjustmentTime,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnFilters<double> get latitude => $composableBuilder(
|
||||||
|
column: $table.latitude,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnFilters<double> get longitude => $composableBuilder(
|
||||||
|
column: $table.longitude,
|
||||||
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i3.$$RemoteAssetEntityTableFilterComposer get assetId {
|
||||||
|
final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.assetId,
|
||||||
|
referencedTable: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => i3.$$RemoteAssetEntityTableFilterComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$RemoteAssetCloudIdEntityTableOrderingComposer
|
||||||
|
extends
|
||||||
|
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
|
||||||
|
$$RemoteAssetCloudIdEntityTableOrderingComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.ColumnOrderings<String> get cloudId => $composableBuilder(
|
||||||
|
column: $table.cloudId,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
|
||||||
|
column: $table.createdAt,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnOrderings<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
|
column: $table.adjustmentTime,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnOrderings<double> get latitude => $composableBuilder(
|
||||||
|
column: $table.latitude,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.ColumnOrderings<double> get longitude => $composableBuilder(
|
||||||
|
column: $table.longitude,
|
||||||
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
|
);
|
||||||
|
|
||||||
|
i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
|
||||||
|
final i3.$$RemoteAssetEntityTableOrderingComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.assetId,
|
||||||
|
referencedTable: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => i3.$$RemoteAssetEntityTableOrderingComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$RemoteAssetCloudIdEntityTableAnnotationComposer
|
||||||
|
extends
|
||||||
|
i0.Composer<i0.GeneratedDatabase, i1.$RemoteAssetCloudIdEntityTable> {
|
||||||
|
$$RemoteAssetCloudIdEntityTableAnnotationComposer({
|
||||||
|
required super.$db,
|
||||||
|
required super.$table,
|
||||||
|
super.joinBuilder,
|
||||||
|
super.$addJoinBuilderToRootComposer,
|
||||||
|
super.$removeJoinBuilderFromRootComposer,
|
||||||
|
});
|
||||||
|
i0.GeneratedColumn<String> get cloudId =>
|
||||||
|
$composableBuilder(column: $table.cloudId, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
$composableBuilder(column: $table.createdAt, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<DateTime> get adjustmentTime => $composableBuilder(
|
||||||
|
column: $table.adjustmentTime,
|
||||||
|
builder: (column) => column,
|
||||||
|
);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<double> get latitude =>
|
||||||
|
$composableBuilder(column: $table.latitude, builder: (column) => column);
|
||||||
|
|
||||||
|
i0.GeneratedColumn<double> get longitude =>
|
||||||
|
$composableBuilder(column: $table.longitude, builder: (column) => column);
|
||||||
|
|
||||||
|
i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
|
||||||
|
final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
|
||||||
|
$composerBuilder(
|
||||||
|
composer: this,
|
||||||
|
getCurrentColumn: (t) => t.assetId,
|
||||||
|
referencedTable: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
getReferencedColumn: (t) => t.id,
|
||||||
|
builder:
|
||||||
|
(
|
||||||
|
joinBuilder, {
|
||||||
|
$addJoinBuilderToRootComposer,
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
}) => i3.$$RemoteAssetEntityTableAnnotationComposer(
|
||||||
|
$db: $db,
|
||||||
|
$table: i4.ReadDatabaseContainer(
|
||||||
|
$db,
|
||||||
|
).resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
|
||||||
|
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
||||||
|
joinBuilder: joinBuilder,
|
||||||
|
$removeJoinBuilderFromRootComposer:
|
||||||
|
$removeJoinBuilderFromRootComposer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return composer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class $$RemoteAssetCloudIdEntityTableTableManager
|
||||||
|
extends
|
||||||
|
i0.RootTableManager<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$RemoteAssetCloudIdEntityTable,
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableFilterComposer,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer,
|
||||||
|
$$RemoteAssetCloudIdEntityTableCreateCompanionBuilder,
|
||||||
|
$$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder,
|
||||||
|
(
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableReferences,
|
||||||
|
),
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i0.PrefetchHooks Function({bool assetId})
|
||||||
|
> {
|
||||||
|
$$RemoteAssetCloudIdEntityTableTableManager(
|
||||||
|
i0.GeneratedDatabase db,
|
||||||
|
i1.$RemoteAssetCloudIdEntityTable table,
|
||||||
|
) : super(
|
||||||
|
i0.TableManagerState(
|
||||||
|
db: db,
|
||||||
|
table: table,
|
||||||
|
createFilteringComposer: () =>
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableFilterComposer(
|
||||||
|
$db: db,
|
||||||
|
$table: table,
|
||||||
|
),
|
||||||
|
createOrderingComposer: () =>
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer(
|
||||||
|
$db: db,
|
||||||
|
$table: table,
|
||||||
|
),
|
||||||
|
createComputedFieldComposer: () =>
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer(
|
||||||
|
$db: db,
|
||||||
|
$table: table,
|
||||||
|
),
|
||||||
|
updateCompanionCallback:
|
||||||
|
({
|
||||||
|
i0.Value<String> assetId = const i0.Value.absent(),
|
||||||
|
i0.Value<String?> cloudId = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
|
}) => i1.RemoteAssetCloudIdEntityCompanion(
|
||||||
|
assetId: assetId,
|
||||||
|
cloudId: cloudId,
|
||||||
|
createdAt: createdAt,
|
||||||
|
adjustmentTime: adjustmentTime,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
),
|
||||||
|
createCompanionCallback:
|
||||||
|
({
|
||||||
|
required String assetId,
|
||||||
|
i0.Value<String?> cloudId = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
|
}) => i1.RemoteAssetCloudIdEntityCompanion.insert(
|
||||||
|
assetId: assetId,
|
||||||
|
cloudId: cloudId,
|
||||||
|
createdAt: createdAt,
|
||||||
|
adjustmentTime: adjustmentTime,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
),
|
||||||
|
withReferenceMapper: (p0) => p0
|
||||||
|
.map(
|
||||||
|
(e) => (
|
||||||
|
e.readTable(table),
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableReferences(db, table, e),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
prefetchHooksCallback: ({assetId = false}) {
|
||||||
|
return i0.PrefetchHooks(
|
||||||
|
db: db,
|
||||||
|
explicitlyWatchedTables: [],
|
||||||
|
addJoins:
|
||||||
|
<
|
||||||
|
T extends i0.TableManagerState<
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic,
|
||||||
|
dynamic
|
||||||
|
>
|
||||||
|
>(state) {
|
||||||
|
if (assetId) {
|
||||||
|
state =
|
||||||
|
state.withJoin(
|
||||||
|
currentTable: table,
|
||||||
|
currentColumn: table.assetId,
|
||||||
|
referencedTable: i1
|
||||||
|
.$$RemoteAssetCloudIdEntityTableReferences
|
||||||
|
._assetIdTable(db),
|
||||||
|
referencedColumn: i1
|
||||||
|
.$$RemoteAssetCloudIdEntityTableReferences
|
||||||
|
._assetIdTable(db)
|
||||||
|
.id,
|
||||||
|
)
|
||||||
|
as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
},
|
||||||
|
getPrefetchedDataCallback: (items) async {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef $$RemoteAssetCloudIdEntityTableProcessedTableManager =
|
||||||
|
i0.ProcessedTableManager<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$RemoteAssetCloudIdEntityTable,
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableFilterComposer,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableOrderingComposer,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableAnnotationComposer,
|
||||||
|
$$RemoteAssetCloudIdEntityTableCreateCompanionBuilder,
|
||||||
|
$$RemoteAssetCloudIdEntityTableUpdateCompanionBuilder,
|
||||||
|
(
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i1.$$RemoteAssetCloudIdEntityTableReferences,
|
||||||
|
),
|
||||||
|
i1.RemoteAssetCloudIdEntityData,
|
||||||
|
i0.PrefetchHooks Function({bool assetId})
|
||||||
|
>;
|
||||||
|
|
||||||
|
class $RemoteAssetCloudIdEntityTable extends i2.RemoteAssetCloudIdEntity
|
||||||
|
with
|
||||||
|
i0.TableInfo<
|
||||||
|
$RemoteAssetCloudIdEntityTable,
|
||||||
|
i1.RemoteAssetCloudIdEntityData
|
||||||
|
> {
|
||||||
|
@override
|
||||||
|
final i0.GeneratedDatabase attachedDatabase;
|
||||||
|
final String? _alias;
|
||||||
|
$RemoteAssetCloudIdEntityTable(this.attachedDatabase, [this._alias]);
|
||||||
|
static const i0.VerificationMeta _assetIdMeta = const i0.VerificationMeta(
|
||||||
|
'assetId',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
|
||||||
|
'asset_id',
|
||||||
|
aliasedName,
|
||||||
|
false,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: true,
|
||||||
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
||||||
|
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
static const i0.VerificationMeta _cloudIdMeta = const i0.VerificationMeta(
|
||||||
|
'cloudId',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<String> cloudId = i0.GeneratedColumn<String>(
|
||||||
|
'cloud_id',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.string,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
defaultConstraints: i0.GeneratedColumn.constraintIsAlways('UNIQUE'),
|
||||||
|
);
|
||||||
|
static const i0.VerificationMeta _createdAtMeta = const i0.VerificationMeta(
|
||||||
|
'createdAt',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<DateTime> createdAt =
|
||||||
|
i0.GeneratedColumn<DateTime>(
|
||||||
|
'created_at',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.dateTime,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const i0.VerificationMeta _adjustmentTimeMeta =
|
||||||
|
const i0.VerificationMeta('adjustmentTime');
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<DateTime> adjustmentTime =
|
||||||
|
i0.GeneratedColumn<DateTime>(
|
||||||
|
'adjustment_time',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.dateTime,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const i0.VerificationMeta _latitudeMeta = const i0.VerificationMeta(
|
||||||
|
'latitude',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<double> latitude = i0.GeneratedColumn<double>(
|
||||||
|
'latitude',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.double,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
static const i0.VerificationMeta _longitudeMeta = const i0.VerificationMeta(
|
||||||
|
'longitude',
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
late final i0.GeneratedColumn<double> longitude = i0.GeneratedColumn<double>(
|
||||||
|
'longitude',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i0.DriftSqlType.double,
|
||||||
|
requiredDuringInsert: false,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
List<i0.GeneratedColumn> get $columns => [
|
||||||
|
assetId,
|
||||||
|
cloudId,
|
||||||
|
createdAt,
|
||||||
|
adjustmentTime,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
];
|
||||||
|
@override
|
||||||
|
String get aliasedName => _alias ?? actualTableName;
|
||||||
|
@override
|
||||||
|
String get actualTableName => $name;
|
||||||
|
static const String $name = 'remote_asset_cloud_id_entity';
|
||||||
|
@override
|
||||||
|
i0.VerificationContext validateIntegrity(
|
||||||
|
i0.Insertable<i1.RemoteAssetCloudIdEntityData> instance, {
|
||||||
|
bool isInserting = false,
|
||||||
|
}) {
|
||||||
|
final context = i0.VerificationContext();
|
||||||
|
final data = instance.toColumns(true);
|
||||||
|
if (data.containsKey('asset_id')) {
|
||||||
|
context.handle(
|
||||||
|
_assetIdMeta,
|
||||||
|
assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta),
|
||||||
|
);
|
||||||
|
} else if (isInserting) {
|
||||||
|
context.missing(_assetIdMeta);
|
||||||
|
}
|
||||||
|
if (data.containsKey('cloud_id')) {
|
||||||
|
context.handle(
|
||||||
|
_cloudIdMeta,
|
||||||
|
cloudId.isAcceptableOrUnknown(data['cloud_id']!, _cloudIdMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('created_at')) {
|
||||||
|
context.handle(
|
||||||
|
_createdAtMeta,
|
||||||
|
createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('adjustment_time')) {
|
||||||
|
context.handle(
|
||||||
|
_adjustmentTimeMeta,
|
||||||
|
adjustmentTime.isAcceptableOrUnknown(
|
||||||
|
data['adjustment_time']!,
|
||||||
|
_adjustmentTimeMeta,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('latitude')) {
|
||||||
|
context.handle(
|
||||||
|
_latitudeMeta,
|
||||||
|
latitude.isAcceptableOrUnknown(data['latitude']!, _latitudeMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (data.containsKey('longitude')) {
|
||||||
|
context.handle(
|
||||||
|
_longitudeMeta,
|
||||||
|
longitude.isAcceptableOrUnknown(data['longitude']!, _longitudeMeta),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Set<i0.GeneratedColumn> get $primaryKey => {assetId};
|
||||||
|
@override
|
||||||
|
i1.RemoteAssetCloudIdEntityData map(
|
||||||
|
Map<String, dynamic> data, {
|
||||||
|
String? tablePrefix,
|
||||||
|
}) {
|
||||||
|
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
|
||||||
|
return i1.RemoteAssetCloudIdEntityData(
|
||||||
|
assetId: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}asset_id'],
|
||||||
|
)!,
|
||||||
|
cloudId: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.string,
|
||||||
|
data['${effectivePrefix}cloud_id'],
|
||||||
|
),
|
||||||
|
createdAt: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.dateTime,
|
||||||
|
data['${effectivePrefix}created_at'],
|
||||||
|
),
|
||||||
|
adjustmentTime: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.dateTime,
|
||||||
|
data['${effectivePrefix}adjustment_time'],
|
||||||
|
),
|
||||||
|
latitude: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.double,
|
||||||
|
data['${effectivePrefix}latitude'],
|
||||||
|
),
|
||||||
|
longitude: attachedDatabase.typeMapping.read(
|
||||||
|
i0.DriftSqlType.double,
|
||||||
|
data['${effectivePrefix}longitude'],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
$RemoteAssetCloudIdEntityTable createAlias(String alias) {
|
||||||
|
return $RemoteAssetCloudIdEntityTable(attachedDatabase, alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get withoutRowId => true;
|
||||||
|
@override
|
||||||
|
bool get isStrict => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RemoteAssetCloudIdEntityData extends i0.DataClass
|
||||||
|
implements i0.Insertable<i1.RemoteAssetCloudIdEntityData> {
|
||||||
|
final String assetId;
|
||||||
|
final String? cloudId;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final DateTime? adjustmentTime;
|
||||||
|
final double? latitude;
|
||||||
|
final double? longitude;
|
||||||
|
const RemoteAssetCloudIdEntityData({
|
||||||
|
required this.assetId,
|
||||||
|
this.cloudId,
|
||||||
|
this.createdAt,
|
||||||
|
this.adjustmentTime,
|
||||||
|
this.latitude,
|
||||||
|
this.longitude,
|
||||||
|
});
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
map['asset_id'] = i0.Variable<String>(assetId);
|
||||||
|
if (!nullToAbsent || cloudId != null) {
|
||||||
|
map['cloud_id'] = i0.Variable<String>(cloudId);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || createdAt != null) {
|
||||||
|
map['created_at'] = i0.Variable<DateTime>(createdAt);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || adjustmentTime != null) {
|
||||||
|
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || latitude != null) {
|
||||||
|
map['latitude'] = i0.Variable<double>(latitude);
|
||||||
|
}
|
||||||
|
if (!nullToAbsent || longitude != null) {
|
||||||
|
map['longitude'] = i0.Variable<double>(longitude);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
factory RemoteAssetCloudIdEntityData.fromJson(
|
||||||
|
Map<String, dynamic> json, {
|
||||||
|
i0.ValueSerializer? serializer,
|
||||||
|
}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return RemoteAssetCloudIdEntityData(
|
||||||
|
assetId: serializer.fromJson<String>(json['assetId']),
|
||||||
|
cloudId: serializer.fromJson<String?>(json['cloudId']),
|
||||||
|
createdAt: serializer.fromJson<DateTime?>(json['createdAt']),
|
||||||
|
adjustmentTime: serializer.fromJson<DateTime?>(json['adjustmentTime']),
|
||||||
|
latitude: serializer.fromJson<double?>(json['latitude']),
|
||||||
|
longitude: serializer.fromJson<double?>(json['longitude']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
|
||||||
|
serializer ??= i0.driftRuntimeOptions.defaultSerializer;
|
||||||
|
return <String, dynamic>{
|
||||||
|
'assetId': serializer.toJson<String>(assetId),
|
||||||
|
'cloudId': serializer.toJson<String?>(cloudId),
|
||||||
|
'createdAt': serializer.toJson<DateTime?>(createdAt),
|
||||||
|
'adjustmentTime': serializer.toJson<DateTime?>(adjustmentTime),
|
||||||
|
'latitude': serializer.toJson<double?>(latitude),
|
||||||
|
'longitude': serializer.toJson<double?>(longitude),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.RemoteAssetCloudIdEntityData copyWith({
|
||||||
|
String? assetId,
|
||||||
|
i0.Value<String?> cloudId = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> createdAt = const i0.Value.absent(),
|
||||||
|
i0.Value<DateTime?> adjustmentTime = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> latitude = const i0.Value.absent(),
|
||||||
|
i0.Value<double?> longitude = const i0.Value.absent(),
|
||||||
|
}) => i1.RemoteAssetCloudIdEntityData(
|
||||||
|
assetId: assetId ?? this.assetId,
|
||||||
|
cloudId: cloudId.present ? cloudId.value : this.cloudId,
|
||||||
|
createdAt: createdAt.present ? createdAt.value : this.createdAt,
|
||||||
|
adjustmentTime: adjustmentTime.present
|
||||||
|
? adjustmentTime.value
|
||||||
|
: this.adjustmentTime,
|
||||||
|
latitude: latitude.present ? latitude.value : this.latitude,
|
||||||
|
longitude: longitude.present ? longitude.value : this.longitude,
|
||||||
|
);
|
||||||
|
RemoteAssetCloudIdEntityData copyWithCompanion(
|
||||||
|
i1.RemoteAssetCloudIdEntityCompanion data,
|
||||||
|
) {
|
||||||
|
return RemoteAssetCloudIdEntityData(
|
||||||
|
assetId: data.assetId.present ? data.assetId.value : this.assetId,
|
||||||
|
cloudId: data.cloudId.present ? data.cloudId.value : this.cloudId,
|
||||||
|
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
|
||||||
|
adjustmentTime: data.adjustmentTime.present
|
||||||
|
? data.adjustmentTime.value
|
||||||
|
: this.adjustmentTime,
|
||||||
|
latitude: data.latitude.present ? data.latitude.value : this.latitude,
|
||||||
|
longitude: data.longitude.present ? data.longitude.value : this.longitude,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('RemoteAssetCloudIdEntityData(')
|
||||||
|
..write('assetId: $assetId, ')
|
||||||
|
..write('cloudId: $cloudId, ')
|
||||||
|
..write('createdAt: $createdAt, ')
|
||||||
|
..write('adjustmentTime: $adjustmentTime, ')
|
||||||
|
..write('latitude: $latitude, ')
|
||||||
|
..write('longitude: $longitude')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(
|
||||||
|
assetId,
|
||||||
|
cloudId,
|
||||||
|
createdAt,
|
||||||
|
adjustmentTime,
|
||||||
|
latitude,
|
||||||
|
longitude,
|
||||||
|
);
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
(other is i1.RemoteAssetCloudIdEntityData &&
|
||||||
|
other.assetId == this.assetId &&
|
||||||
|
other.cloudId == this.cloudId &&
|
||||||
|
other.createdAt == this.createdAt &&
|
||||||
|
other.adjustmentTime == this.adjustmentTime &&
|
||||||
|
other.latitude == this.latitude &&
|
||||||
|
other.longitude == this.longitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
class RemoteAssetCloudIdEntityCompanion
|
||||||
|
extends i0.UpdateCompanion<i1.RemoteAssetCloudIdEntityData> {
|
||||||
|
final i0.Value<String> assetId;
|
||||||
|
final i0.Value<String?> cloudId;
|
||||||
|
final i0.Value<DateTime?> createdAt;
|
||||||
|
final i0.Value<DateTime?> adjustmentTime;
|
||||||
|
final i0.Value<double?> latitude;
|
||||||
|
final i0.Value<double?> longitude;
|
||||||
|
const RemoteAssetCloudIdEntityCompanion({
|
||||||
|
this.assetId = const i0.Value.absent(),
|
||||||
|
this.cloudId = const i0.Value.absent(),
|
||||||
|
this.createdAt = const i0.Value.absent(),
|
||||||
|
this.adjustmentTime = const i0.Value.absent(),
|
||||||
|
this.latitude = const i0.Value.absent(),
|
||||||
|
this.longitude = const i0.Value.absent(),
|
||||||
|
});
|
||||||
|
RemoteAssetCloudIdEntityCompanion.insert({
|
||||||
|
required String assetId,
|
||||||
|
this.cloudId = const i0.Value.absent(),
|
||||||
|
this.createdAt = const i0.Value.absent(),
|
||||||
|
this.adjustmentTime = const i0.Value.absent(),
|
||||||
|
this.latitude = const i0.Value.absent(),
|
||||||
|
this.longitude = const i0.Value.absent(),
|
||||||
|
}) : assetId = i0.Value(assetId);
|
||||||
|
static i0.Insertable<i1.RemoteAssetCloudIdEntityData> custom({
|
||||||
|
i0.Expression<String>? assetId,
|
||||||
|
i0.Expression<String>? cloudId,
|
||||||
|
i0.Expression<DateTime>? createdAt,
|
||||||
|
i0.Expression<DateTime>? adjustmentTime,
|
||||||
|
i0.Expression<double>? latitude,
|
||||||
|
i0.Expression<double>? longitude,
|
||||||
|
}) {
|
||||||
|
return i0.RawValuesInsertable({
|
||||||
|
if (assetId != null) 'asset_id': assetId,
|
||||||
|
if (cloudId != null) 'cloud_id': cloudId,
|
||||||
|
if (createdAt != null) 'created_at': createdAt,
|
||||||
|
if (adjustmentTime != null) 'adjustment_time': adjustmentTime,
|
||||||
|
if (latitude != null) 'latitude': latitude,
|
||||||
|
if (longitude != null) 'longitude': longitude,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.RemoteAssetCloudIdEntityCompanion copyWith({
|
||||||
|
i0.Value<String>? assetId,
|
||||||
|
i0.Value<String?>? cloudId,
|
||||||
|
i0.Value<DateTime?>? createdAt,
|
||||||
|
i0.Value<DateTime?>? adjustmentTime,
|
||||||
|
i0.Value<double?>? latitude,
|
||||||
|
i0.Value<double?>? longitude,
|
||||||
|
}) {
|
||||||
|
return i1.RemoteAssetCloudIdEntityCompanion(
|
||||||
|
assetId: assetId ?? this.assetId,
|
||||||
|
cloudId: cloudId ?? this.cloudId,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
adjustmentTime: adjustmentTime ?? this.adjustmentTime,
|
||||||
|
latitude: latitude ?? this.latitude,
|
||||||
|
longitude: longitude ?? this.longitude,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, i0.Expression> toColumns(bool nullToAbsent) {
|
||||||
|
final map = <String, i0.Expression>{};
|
||||||
|
if (assetId.present) {
|
||||||
|
map['asset_id'] = i0.Variable<String>(assetId.value);
|
||||||
|
}
|
||||||
|
if (cloudId.present) {
|
||||||
|
map['cloud_id'] = i0.Variable<String>(cloudId.value);
|
||||||
|
}
|
||||||
|
if (createdAt.present) {
|
||||||
|
map['created_at'] = i0.Variable<DateTime>(createdAt.value);
|
||||||
|
}
|
||||||
|
if (adjustmentTime.present) {
|
||||||
|
map['adjustment_time'] = i0.Variable<DateTime>(adjustmentTime.value);
|
||||||
|
}
|
||||||
|
if (latitude.present) {
|
||||||
|
map['latitude'] = i0.Variable<double>(latitude.value);
|
||||||
|
}
|
||||||
|
if (longitude.present) {
|
||||||
|
map['longitude'] = i0.Variable<double>(longitude.value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return (StringBuffer('RemoteAssetCloudIdEntityCompanion(')
|
||||||
|
..write('assetId: $assetId, ')
|
||||||
|
..write('cloudId: $cloudId, ')
|
||||||
|
..write('createdAt: $createdAt, ')
|
||||||
|
..write('adjustmentTime: $adjustmentTime, ')
|
||||||
|
..write('latitude: $latitude, ')
|
||||||
|
..write('longitude: $longitude')
|
||||||
|
..write(')'))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ 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_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';
|
||||||
@@ -57,6 +58,7 @@ class IsarDatabaseRepository implements IDatabaseRepository {
|
|||||||
RemoteAlbumEntity,
|
RemoteAlbumEntity,
|
||||||
RemoteAlbumAssetEntity,
|
RemoteAlbumAssetEntity,
|
||||||
RemoteAlbumUserEntity,
|
RemoteAlbumUserEntity,
|
||||||
|
RemoteAssetCloudIdEntity,
|
||||||
MemoryEntity,
|
MemoryEntity,
|
||||||
MemoryAssetEntity,
|
MemoryAssetEntity,
|
||||||
StackEntity,
|
StackEntity,
|
||||||
@@ -95,7 +97,7 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 14;
|
int get schemaVersion => 15;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
@@ -190,6 +192,12 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||||||
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.latitude);
|
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.latitude);
|
||||||
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.longitude);
|
await m.addColumn(v14.localAssetEntity, v14.localAssetEntity.longitude);
|
||||||
},
|
},
|
||||||
|
from14To15: (m, v15) async {
|
||||||
|
// Add i_cloud_id to local and remote asset tables
|
||||||
|
await m.addColumn(v15.localAssetEntity, v15.localAssetEntity.iCloudId);
|
||||||
|
await m.createIndex(v15.idxLocalAssetCloudId);
|
||||||
|
await m.createTable(v15.remoteAssetCloudIdEntity);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -27,21 +27,23 @@ import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.
|
|||||||
as i12;
|
as i12;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart'
|
||||||
as i13;
|
as i13;
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart'
|
||||||
as i14;
|
as i14;
|
||||||
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/memory.entity.drift.dart'
|
||||||
as i15;
|
as i15;
|
||||||
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/memory_asset.entity.drift.dart'
|
||||||
as i16;
|
as i16;
|
||||||
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart'
|
||||||
as i17;
|
as i17;
|
||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/asset_face.entity.drift.dart'
|
||||||
as i18;
|
as i18;
|
||||||
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'
|
||||||
as i19;
|
as i19;
|
||||||
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/trashed_local_asset.entity.drift.dart'
|
||||||
as i20;
|
as i20;
|
||||||
import 'package:drift/internal/modular.dart' as i21;
|
import 'package:immich_mobile/infrastructure/entities/merged_asset.drift.dart'
|
||||||
|
as i21;
|
||||||
|
import 'package:drift/internal/modular.dart' as i22;
|
||||||
|
|
||||||
abstract class $Drift extends i0.GeneratedDatabase {
|
abstract class $Drift extends i0.GeneratedDatabase {
|
||||||
$Drift(i0.QueryExecutor e) : super(e);
|
$Drift(i0.QueryExecutor e) : super(e);
|
||||||
@@ -72,18 +74,20 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
.$RemoteAlbumAssetEntityTable(this);
|
.$RemoteAlbumAssetEntityTable(this);
|
||||||
late final i13.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i13
|
late final i13.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i13
|
||||||
.$RemoteAlbumUserEntityTable(this);
|
.$RemoteAlbumUserEntityTable(this);
|
||||||
late final i14.$MemoryEntityTable memoryEntity = i14.$MemoryEntityTable(this);
|
late final i14.$RemoteAssetCloudIdEntityTable remoteAssetCloudIdEntity = i14
|
||||||
late final i15.$MemoryAssetEntityTable memoryAssetEntity = i15
|
.$RemoteAssetCloudIdEntityTable(this);
|
||||||
|
late final i15.$MemoryEntityTable memoryEntity = i15.$MemoryEntityTable(this);
|
||||||
|
late final i16.$MemoryAssetEntityTable memoryAssetEntity = i16
|
||||||
.$MemoryAssetEntityTable(this);
|
.$MemoryAssetEntityTable(this);
|
||||||
late final i16.$PersonEntityTable personEntity = i16.$PersonEntityTable(this);
|
late final i17.$PersonEntityTable personEntity = i17.$PersonEntityTable(this);
|
||||||
late final i17.$AssetFaceEntityTable assetFaceEntity = i17
|
late final i18.$AssetFaceEntityTable assetFaceEntity = i18
|
||||||
.$AssetFaceEntityTable(this);
|
.$AssetFaceEntityTable(this);
|
||||||
late final i18.$StoreEntityTable storeEntity = i18.$StoreEntityTable(this);
|
late final i19.$StoreEntityTable storeEntity = i19.$StoreEntityTable(this);
|
||||||
late final i19.$TrashedLocalAssetEntityTable trashedLocalAssetEntity = i19
|
late final i20.$TrashedLocalAssetEntityTable trashedLocalAssetEntity = i20
|
||||||
.$TrashedLocalAssetEntityTable(this);
|
.$TrashedLocalAssetEntityTable(this);
|
||||||
i20.MergedAssetDrift get mergedAssetDrift => i21.ReadDatabaseContainer(
|
i21.MergedAssetDrift get mergedAssetDrift => i22.ReadDatabaseContainer(
|
||||||
this,
|
this,
|
||||||
).accessor<i20.MergedAssetDrift>(i20.MergedAssetDrift.new);
|
).accessor<i21.MergedAssetDrift>(i21.MergedAssetDrift.new);
|
||||||
@override
|
@override
|
||||||
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
|
||||||
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
|
||||||
@@ -97,6 +101,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
localAlbumEntity,
|
localAlbumEntity,
|
||||||
localAlbumAssetEntity,
|
localAlbumAssetEntity,
|
||||||
i4.idxLocalAssetChecksum,
|
i4.idxLocalAssetChecksum,
|
||||||
|
i4.idxLocalAssetCloudId,
|
||||||
i2.idxRemoteAssetOwnerChecksum,
|
i2.idxRemoteAssetOwnerChecksum,
|
||||||
i2.uQRemoteAssetsOwnerChecksum,
|
i2.uQRemoteAssetsOwnerChecksum,
|
||||||
i2.uQRemoteAssetsOwnerLibraryChecksum,
|
i2.uQRemoteAssetsOwnerLibraryChecksum,
|
||||||
@@ -107,6 +112,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
remoteExifEntity,
|
remoteExifEntity,
|
||||||
remoteAlbumAssetEntity,
|
remoteAlbumAssetEntity,
|
||||||
remoteAlbumUserEntity,
|
remoteAlbumUserEntity,
|
||||||
|
remoteAssetCloudIdEntity,
|
||||||
memoryEntity,
|
memoryEntity,
|
||||||
memoryAssetEntity,
|
memoryAssetEntity,
|
||||||
personEntity,
|
personEntity,
|
||||||
@@ -114,8 +120,8 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
storeEntity,
|
storeEntity,
|
||||||
trashedLocalAssetEntity,
|
trashedLocalAssetEntity,
|
||||||
i11.idxLatLng,
|
i11.idxLatLng,
|
||||||
i19.idxTrashedLocalAssetChecksum,
|
i20.idxTrashedLocalAssetChecksum,
|
||||||
i19.idxTrashedLocalAssetAlbum,
|
i20.idxTrashedLocalAssetAlbum,
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
i0.StreamQueryUpdateRules
|
i0.StreamQueryUpdateRules
|
||||||
@@ -249,6 +255,18 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete),
|
i0.TableUpdate('remote_album_user_entity', kind: i0.UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
|
'remote_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete,
|
||||||
|
),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate(
|
||||||
|
'remote_asset_cloud_id_entity',
|
||||||
|
kind: i0.UpdateKind.delete,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
i0.WritePropagation(
|
i0.WritePropagation(
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
'user_entity',
|
'user_entity',
|
||||||
@@ -333,18 +351,24 @@ class $DriftManager {
|
|||||||
);
|
);
|
||||||
i13.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i13
|
i13.$$RemoteAlbumUserEntityTableTableManager get remoteAlbumUserEntity => i13
|
||||||
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
|
.$$RemoteAlbumUserEntityTableTableManager(_db, _db.remoteAlbumUserEntity);
|
||||||
i14.$$MemoryEntityTableTableManager get memoryEntity =>
|
i14.$$RemoteAssetCloudIdEntityTableTableManager
|
||||||
i14.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
|
get remoteAssetCloudIdEntity =>
|
||||||
i15.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
|
i14.$$RemoteAssetCloudIdEntityTableTableManager(
|
||||||
i15.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
|
_db,
|
||||||
i16.$$PersonEntityTableTableManager get personEntity =>
|
_db.remoteAssetCloudIdEntity,
|
||||||
i16.$$PersonEntityTableTableManager(_db, _db.personEntity);
|
);
|
||||||
i17.$$AssetFaceEntityTableTableManager get assetFaceEntity =>
|
i15.$$MemoryEntityTableTableManager get memoryEntity =>
|
||||||
i17.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity);
|
i15.$$MemoryEntityTableTableManager(_db, _db.memoryEntity);
|
||||||
i18.$$StoreEntityTableTableManager get storeEntity =>
|
i16.$$MemoryAssetEntityTableTableManager get memoryAssetEntity =>
|
||||||
i18.$$StoreEntityTableTableManager(_db, _db.storeEntity);
|
i16.$$MemoryAssetEntityTableTableManager(_db, _db.memoryAssetEntity);
|
||||||
i19.$$TrashedLocalAssetEntityTableTableManager get trashedLocalAssetEntity =>
|
i17.$$PersonEntityTableTableManager get personEntity =>
|
||||||
i19.$$TrashedLocalAssetEntityTableTableManager(
|
i17.$$PersonEntityTableTableManager(_db, _db.personEntity);
|
||||||
|
i18.$$AssetFaceEntityTableTableManager get assetFaceEntity =>
|
||||||
|
i18.$$AssetFaceEntityTableTableManager(_db, _db.assetFaceEntity);
|
||||||
|
i19.$$StoreEntityTableTableManager get storeEntity =>
|
||||||
|
i19.$$StoreEntityTableTableManager(_db, _db.storeEntity);
|
||||||
|
i20.$$TrashedLocalAssetEntityTableTableManager get trashedLocalAssetEntity =>
|
||||||
|
i20.$$TrashedLocalAssetEntityTableTableManager(
|
||||||
_db,
|
_db,
|
||||||
_db.trashedLocalAssetEntity,
|
_db.trashedLocalAssetEntity,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5941,6 +5941,521 @@ i1.GeneratedColumn<DateTime> _column_96(String aliasedName) =>
|
|||||||
true,
|
true,
|
||||||
type: i1.DriftSqlType.dateTime,
|
type: i1.DriftSqlType.dateTime,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final class Schema15 extends i0.VersionedSchema {
|
||||||
|
Schema15({required super.database}) : super(version: 15);
|
||||||
|
@override
|
||||||
|
late final List<i1.DatabaseSchemaEntity> entities = [
|
||||||
|
userEntity,
|
||||||
|
remoteAssetEntity,
|
||||||
|
stackEntity,
|
||||||
|
localAssetEntity,
|
||||||
|
remoteAlbumEntity,
|
||||||
|
localAlbumEntity,
|
||||||
|
localAlbumAssetEntity,
|
||||||
|
idxLocalAssetChecksum,
|
||||||
|
idxLocalAssetCloudId,
|
||||||
|
idxRemoteAssetOwnerChecksum,
|
||||||
|
uQRemoteAssetsOwnerChecksum,
|
||||||
|
uQRemoteAssetsOwnerLibraryChecksum,
|
||||||
|
idxRemoteAssetChecksum,
|
||||||
|
authUserEntity,
|
||||||
|
userMetadataEntity,
|
||||||
|
partnerEntity,
|
||||||
|
remoteExifEntity,
|
||||||
|
remoteAlbumAssetEntity,
|
||||||
|
remoteAlbumUserEntity,
|
||||||
|
remoteAssetCloudIdEntity,
|
||||||
|
memoryEntity,
|
||||||
|
memoryAssetEntity,
|
||||||
|
personEntity,
|
||||||
|
assetFaceEntity,
|
||||||
|
storeEntity,
|
||||||
|
trashedLocalAssetEntity,
|
||||||
|
idxLatLng,
|
||||||
|
idxTrashedLocalAssetChecksum,
|
||||||
|
idxTrashedLocalAssetAlbum,
|
||||||
|
];
|
||||||
|
late final Shape20 userEntity = Shape20(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_3,
|
||||||
|
_column_84,
|
||||||
|
_column_85,
|
||||||
|
_column_91,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape17 remoteAssetEntity = Shape17(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_1,
|
||||||
|
_column_8,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_10,
|
||||||
|
_column_11,
|
||||||
|
_column_12,
|
||||||
|
_column_0,
|
||||||
|
_column_13,
|
||||||
|
_column_14,
|
||||||
|
_column_15,
|
||||||
|
_column_16,
|
||||||
|
_column_17,
|
||||||
|
_column_18,
|
||||||
|
_column_19,
|
||||||
|
_column_20,
|
||||||
|
_column_21,
|
||||||
|
_column_86,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape3 stackEntity = Shape3(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'stack_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [_column_0, _column_9, _column_5, _column_15, _column_75],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape25 localAssetEntity = Shape25(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'local_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_1,
|
||||||
|
_column_8,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_10,
|
||||||
|
_column_11,
|
||||||
|
_column_12,
|
||||||
|
_column_0,
|
||||||
|
_column_22,
|
||||||
|
_column_14,
|
||||||
|
_column_23,
|
||||||
|
_column_97,
|
||||||
|
_column_96,
|
||||||
|
_column_46,
|
||||||
|
_column_47,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape9 remoteAlbumEntity = Shape9(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_album_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_56,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_15,
|
||||||
|
_column_57,
|
||||||
|
_column_58,
|
||||||
|
_column_59,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape19 localAlbumEntity = Shape19(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'local_album_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_5,
|
||||||
|
_column_31,
|
||||||
|
_column_32,
|
||||||
|
_column_90,
|
||||||
|
_column_33,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape22 localAlbumAssetEntity = Shape22(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'local_album_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
|
||||||
|
columns: [_column_34, _column_35, _column_33],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
final i1.Index idxLocalAssetChecksum = i1.Index(
|
||||||
|
'idx_local_asset_checksum',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_local_asset_checksum ON local_asset_entity (checksum)',
|
||||||
|
);
|
||||||
|
final i1.Index idxLocalAssetCloudId = i1.Index(
|
||||||
|
'idx_local_asset_cloud_id',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_local_asset_cloud_id ON local_asset_entity (i_cloud_id)',
|
||||||
|
);
|
||||||
|
final i1.Index idxRemoteAssetOwnerChecksum = i1.Index(
|
||||||
|
'idx_remote_asset_owner_checksum',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_remote_asset_owner_checksum ON remote_asset_entity (owner_id, checksum)',
|
||||||
|
);
|
||||||
|
final i1.Index uQRemoteAssetsOwnerChecksum = i1.Index(
|
||||||
|
'UQ_remote_assets_owner_checksum',
|
||||||
|
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_checksum ON remote_asset_entity (owner_id, checksum) WHERE(library_id IS NULL)',
|
||||||
|
);
|
||||||
|
final i1.Index uQRemoteAssetsOwnerLibraryChecksum = i1.Index(
|
||||||
|
'UQ_remote_assets_owner_library_checksum',
|
||||||
|
'CREATE UNIQUE INDEX IF NOT EXISTS UQ_remote_assets_owner_library_checksum ON remote_asset_entity (owner_id, library_id, checksum) WHERE(library_id IS NOT NULL)',
|
||||||
|
);
|
||||||
|
final i1.Index idxRemoteAssetChecksum = i1.Index(
|
||||||
|
'idx_remote_asset_checksum',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_remote_asset_checksum ON remote_asset_entity (checksum)',
|
||||||
|
);
|
||||||
|
late final Shape21 authUserEntity = Shape21(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'auth_user_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_1,
|
||||||
|
_column_3,
|
||||||
|
_column_2,
|
||||||
|
_column_84,
|
||||||
|
_column_85,
|
||||||
|
_column_92,
|
||||||
|
_column_93,
|
||||||
|
_column_7,
|
||||||
|
_column_94,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape4 userMetadataEntity = Shape4(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'user_metadata_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(user_id, "key")'],
|
||||||
|
columns: [_column_25, _column_26, _column_27],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape5 partnerEntity = Shape5(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'partner_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'],
|
||||||
|
columns: [_column_28, _column_29, _column_30],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape8 remoteExifEntity = Shape8(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_exif_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(asset_id)'],
|
||||||
|
columns: [
|
||||||
|
_column_36,
|
||||||
|
_column_37,
|
||||||
|
_column_38,
|
||||||
|
_column_39,
|
||||||
|
_column_40,
|
||||||
|
_column_41,
|
||||||
|
_column_11,
|
||||||
|
_column_10,
|
||||||
|
_column_42,
|
||||||
|
_column_43,
|
||||||
|
_column_44,
|
||||||
|
_column_45,
|
||||||
|
_column_46,
|
||||||
|
_column_47,
|
||||||
|
_column_48,
|
||||||
|
_column_49,
|
||||||
|
_column_50,
|
||||||
|
_column_51,
|
||||||
|
_column_52,
|
||||||
|
_column_53,
|
||||||
|
_column_54,
|
||||||
|
_column_55,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape7 remoteAlbumAssetEntity = Shape7(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_album_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
|
||||||
|
columns: [_column_36, _column_60],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape10 remoteAlbumUserEntity = Shape10(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_album_user_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(album_id, user_id)'],
|
||||||
|
columns: [_column_60, _column_25, _column_61],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape26 remoteAssetCloudIdEntity = Shape26(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'remote_asset_cloud_id_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(asset_id)'],
|
||||||
|
columns: [
|
||||||
|
_column_36,
|
||||||
|
_column_98,
|
||||||
|
_column_99,
|
||||||
|
_column_96,
|
||||||
|
_column_46,
|
||||||
|
_column_47,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape11 memoryEntity = Shape11(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'memory_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_18,
|
||||||
|
_column_15,
|
||||||
|
_column_8,
|
||||||
|
_column_62,
|
||||||
|
_column_63,
|
||||||
|
_column_64,
|
||||||
|
_column_65,
|
||||||
|
_column_66,
|
||||||
|
_column_67,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape12 memoryAssetEntity = Shape12(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'memory_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'],
|
||||||
|
columns: [_column_36, _column_68],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape14 personEntity = Shape14(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'person_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_15,
|
||||||
|
_column_1,
|
||||||
|
_column_69,
|
||||||
|
_column_71,
|
||||||
|
_column_72,
|
||||||
|
_column_73,
|
||||||
|
_column_74,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape15 assetFaceEntity = Shape15(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'asset_face_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [
|
||||||
|
_column_0,
|
||||||
|
_column_36,
|
||||||
|
_column_76,
|
||||||
|
_column_77,
|
||||||
|
_column_78,
|
||||||
|
_column_79,
|
||||||
|
_column_80,
|
||||||
|
_column_81,
|
||||||
|
_column_82,
|
||||||
|
_column_83,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape18 storeEntity = Shape18(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'store_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id)'],
|
||||||
|
columns: [_column_87, _column_88, _column_89],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
late final Shape23 trashedLocalAssetEntity = Shape23(
|
||||||
|
source: i0.VersionedTable(
|
||||||
|
entityName: 'trashed_local_asset_entity',
|
||||||
|
withoutRowId: true,
|
||||||
|
isStrict: true,
|
||||||
|
tableConstraints: ['PRIMARY KEY(id, album_id)'],
|
||||||
|
columns: [
|
||||||
|
_column_1,
|
||||||
|
_column_8,
|
||||||
|
_column_9,
|
||||||
|
_column_5,
|
||||||
|
_column_10,
|
||||||
|
_column_11,
|
||||||
|
_column_12,
|
||||||
|
_column_0,
|
||||||
|
_column_95,
|
||||||
|
_column_22,
|
||||||
|
_column_14,
|
||||||
|
_column_23,
|
||||||
|
],
|
||||||
|
attachedDatabase: database,
|
||||||
|
),
|
||||||
|
alias: null,
|
||||||
|
);
|
||||||
|
final i1.Index idxLatLng = i1.Index(
|
||||||
|
'idx_lat_lng',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)',
|
||||||
|
);
|
||||||
|
final i1.Index idxTrashedLocalAssetChecksum = i1.Index(
|
||||||
|
'idx_trashed_local_asset_checksum',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_checksum ON trashed_local_asset_entity (checksum)',
|
||||||
|
);
|
||||||
|
final i1.Index idxTrashedLocalAssetAlbum = i1.Index(
|
||||||
|
'idx_trashed_local_asset_album',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_trashed_local_asset_album ON trashed_local_asset_entity (album_id)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Shape25 extends i0.VersionedTable {
|
||||||
|
Shape25({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<String> get name =>
|
||||||
|
columnsByName['name']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<int> get type =>
|
||||||
|
columnsByName['type']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<DateTime> get updatedAt =>
|
||||||
|
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<int> get width =>
|
||||||
|
columnsByName['width']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get height =>
|
||||||
|
columnsByName['height']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<int> get durationInSeconds =>
|
||||||
|
columnsByName['duration_in_seconds']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get id =>
|
||||||
|
columnsByName['id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get checksum =>
|
||||||
|
columnsByName['checksum']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<bool> get isFavorite =>
|
||||||
|
columnsByName['is_favorite']! as i1.GeneratedColumn<bool>;
|
||||||
|
i1.GeneratedColumn<int> get orientation =>
|
||||||
|
columnsByName['orientation']! as i1.GeneratedColumn<int>;
|
||||||
|
i1.GeneratedColumn<String> get iCloudId =>
|
||||||
|
columnsByName['i_cloud_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get adjustmentTime =>
|
||||||
|
columnsByName['adjustment_time']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<double> get latitude =>
|
||||||
|
columnsByName['latitude']! as i1.GeneratedColumn<double>;
|
||||||
|
i1.GeneratedColumn<double> get longitude =>
|
||||||
|
columnsByName['longitude']! as i1.GeneratedColumn<double>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_97(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>(
|
||||||
|
'i_cloud_id',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
);
|
||||||
|
|
||||||
|
class Shape26 extends i0.VersionedTable {
|
||||||
|
Shape26({required super.source, required super.alias}) : super.aliased();
|
||||||
|
i1.GeneratedColumn<String> get assetId =>
|
||||||
|
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<String> get cloudId =>
|
||||||
|
columnsByName['cloud_id']! as i1.GeneratedColumn<String>;
|
||||||
|
i1.GeneratedColumn<DateTime> get createdAt =>
|
||||||
|
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<DateTime> get adjustmentTime =>
|
||||||
|
columnsByName['adjustment_time']! as i1.GeneratedColumn<DateTime>;
|
||||||
|
i1.GeneratedColumn<double> get latitude =>
|
||||||
|
columnsByName['latitude']! as i1.GeneratedColumn<double>;
|
||||||
|
i1.GeneratedColumn<double> get longitude =>
|
||||||
|
columnsByName['longitude']! as i1.GeneratedColumn<double>;
|
||||||
|
}
|
||||||
|
|
||||||
|
i1.GeneratedColumn<String> _column_98(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<String>(
|
||||||
|
'cloud_id',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i1.DriftSqlType.string,
|
||||||
|
defaultConstraints: i1.GeneratedColumn.constraintIsAlways('UNIQUE'),
|
||||||
|
);
|
||||||
|
i1.GeneratedColumn<DateTime> _column_99(String aliasedName) =>
|
||||||
|
i1.GeneratedColumn<DateTime>(
|
||||||
|
'created_at',
|
||||||
|
aliasedName,
|
||||||
|
true,
|
||||||
|
type: i1.DriftSqlType.dateTime,
|
||||||
|
);
|
||||||
i0.MigrationStepWithVersion migrationSteps({
|
i0.MigrationStepWithVersion migrationSteps({
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
||||||
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
|
||||||
@@ -5955,6 +6470,7 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||||
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
@@ -6023,6 +6539,11 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from13To14(migrator, schema);
|
await from13To14(migrator, schema);
|
||||||
return 14;
|
return 14;
|
||||||
|
case 14:
|
||||||
|
final schema = Schema15(database: database);
|
||||||
|
final migrator = i1.Migrator(database, schema);
|
||||||
|
await from14To15(migrator, schema);
|
||||||
|
return 15;
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
@@ -6043,6 +6564,7 @@ i1.OnUpgrade stepByStep({
|
|||||||
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
required Future<void> Function(i1.Migrator m, Schema12 schema) from11To12,
|
||||||
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
required Future<void> Function(i1.Migrator m, Schema13 schema) from12To13,
|
||||||
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
required Future<void> Function(i1.Migrator m, Schema14 schema) from13To14,
|
||||||
|
required Future<void> Function(i1.Migrator m, Schema15 schema) from14To15,
|
||||||
}) => i0.VersionedSchema.stepByStepHelper(
|
}) => i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
from1To2: from1To2,
|
from1To2: from1To2,
|
||||||
@@ -6058,5 +6580,6 @@ i1.OnUpgrade stepByStep({
|
|||||||
from11To12: from11To12,
|
from11To12: from11To12,
|
||||||
from12To13: from12To13,
|
from12To13: from12To13,
|
||||||
from13To14: from13To14,
|
from13To14: from13To14,
|
||||||
|
from14To15: from14To15,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -246,6 +246,25 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
|
return query.map((row) => row.readTable(_db.localAssetEntity).toDto()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> updateCloudMapping(Map<String, String?> cloudMapping) {
|
||||||
|
if (cloudMapping.isEmpty) {
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _db.batch((batch) {
|
||||||
|
for (final entry in cloudMapping.entries) {
|
||||||
|
final assetId = entry.key;
|
||||||
|
final cloudId = entry.value;
|
||||||
|
|
||||||
|
batch.update(
|
||||||
|
_db.localAssetEntity,
|
||||||
|
LocalAssetEntityCompanion(iCloudId: Value(cloudId)),
|
||||||
|
where: (f) => f.id.equals(assetId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> Function(Iterable<LocalAsset>) get _upsertAssets =>
|
Future<void> Function(Iterable<LocalAsset>) get _upsertAssets =>
|
||||||
CurrentPlatform.isIOS ? _upsertAssetsDarwin : _upsertAssetsAndroid;
|
CurrentPlatform.isIOS ? _upsertAssetsDarwin : _upsertAssetsAndroid;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
@@ -126,4 +128,40 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<LocalAsset>> getEmptyCloudIdAssets() {
|
||||||
|
final query = _db.localAssetEntity.select()..where((row) => row.iCloudId.isNull());
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String>> getHashMappingFromCloudId() async {
|
||||||
|
final query =
|
||||||
|
_db.localAssetEntity.selectOnly().join([
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAssetCloudIdEntity,
|
||||||
|
_db.localAssetEntity.iCloudId.equalsExp(_db.remoteAssetCloudIdEntity.cloudId),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
leftOuterJoin(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
_db.remoteAssetCloudIdEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..addColumns([_db.localAssetEntity.id, _db.remoteAssetEntity.checksum])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetCloudIdEntity.cloudId.isNotNull() &
|
||||||
|
_db.localAssetEntity.checksum.isNull() &
|
||||||
|
((_db.remoteAssetCloudIdEntity.adjustmentTime.isExp(_db.localAssetEntity.adjustmentTime)) &
|
||||||
|
(_db.remoteAssetCloudIdEntity.latitude.isExp(_db.localAssetEntity.latitude)) &
|
||||||
|
(_db.remoteAssetCloudIdEntity.longitude.isExp(_db.localAssetEntity.longitude)) &
|
||||||
|
(_db.remoteAssetCloudIdEntity.createdAt.isExp(_db.localAssetEntity.createdAt))),
|
||||||
|
);
|
||||||
|
final mapping = await query
|
||||||
|
.map(
|
||||||
|
(row) => (assetId: row.read(_db.localAssetEntity.id)!, checksum: row.read(_db.remoteAssetEntity.checksum)!),
|
||||||
|
)
|
||||||
|
.get();
|
||||||
|
return {for (final entry in mapping) entry.assetId: entry.checksum};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class SyncApiRepository {
|
|||||||
SyncRequestType.usersV1,
|
SyncRequestType.usersV1,
|
||||||
SyncRequestType.assetsV1,
|
SyncRequestType.assetsV1,
|
||||||
SyncRequestType.assetExifsV1,
|
SyncRequestType.assetExifsV1,
|
||||||
|
SyncRequestType.assetMetadataV1,
|
||||||
SyncRequestType.partnersV1,
|
SyncRequestType.partnersV1,
|
||||||
SyncRequestType.partnerAssetsV1,
|
SyncRequestType.partnerAssetsV1,
|
||||||
SyncRequestType.partnerAssetExifsV1,
|
SyncRequestType.partnerAssetExifsV1,
|
||||||
@@ -148,6 +149,8 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
|
|||||||
SyncEntityType.assetV1: SyncAssetV1.fromJson,
|
SyncEntityType.assetV1: SyncAssetV1.fromJson,
|
||||||
SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson,
|
SyncEntityType.assetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||||
SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson,
|
SyncEntityType.assetExifV1: SyncAssetExifV1.fromJson,
|
||||||
|
SyncEntityType.assetMetadataV1: SyncAssetMetadataV1.fromJson,
|
||||||
|
SyncEntityType.assetMetadataDeleteV1: SyncAssetMetadataDeleteV1.fromJson,
|
||||||
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
|
||||||
SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson,
|
SyncEntityType.partnerAssetBackfillV1: SyncAssetV1.fromJson,
|
||||||
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.
|
|||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_album_user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_asset_cloud_id.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart';
|
||||||
@@ -54,6 +55,7 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
await _db.authUserEntity.deleteAll();
|
await _db.authUserEntity.deleteAll();
|
||||||
await _db.userEntity.deleteAll();
|
await _db.userEntity.deleteAll();
|
||||||
await _db.userMetadataEntity.deleteAll();
|
await _db.userMetadataEntity.deleteAll();
|
||||||
|
await _db.remoteAssetCloudIdEntity.deleteAll();
|
||||||
});
|
});
|
||||||
await _db.customStatement('PRAGMA foreign_keys = ON');
|
await _db.customStatement('PRAGMA foreign_keys = ON');
|
||||||
});
|
});
|
||||||
@@ -258,6 +260,50 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteAssetsMetadataV1(Iterable<SyncAssetMetadataDeleteV1> data) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) {
|
||||||
|
for (final metadata in data) {
|
||||||
|
if (metadata.key == AssetMetadataKey.mobileApp) {
|
||||||
|
batch.deleteWhere(_db.remoteAssetCloudIdEntity, (row) => row.assetId.equals(metadata.assetId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: deleteAssetsMetadataV1', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateAssetsMetadataV1(Iterable<SyncAssetMetadataV1> data) async {
|
||||||
|
try {
|
||||||
|
await _db.batch((batch) {
|
||||||
|
for (final metadata in data) {
|
||||||
|
if (metadata.key == AssetMetadataKey.mobileApp) {
|
||||||
|
final map = metadata.value as Map<String, Object?>;
|
||||||
|
final companion = RemoteAssetCloudIdEntityCompanion(
|
||||||
|
cloudId: Value(map['iCloudId']?.toString()),
|
||||||
|
createdAt: Value(map['createdAt'] != null ? DateTime.parse(map['createdAt'] as String) : null),
|
||||||
|
adjustmentTime: Value(
|
||||||
|
map['adjustmentTime'] != null ? DateTime.parse(map['adjustmentTime'] as String) : null,
|
||||||
|
),
|
||||||
|
latitude: Value(map['latitude'] != null ? (double.tryParse(map['latitude'] as String)) : null),
|
||||||
|
longitude: Value(map['longitude'] != null ? (double.tryParse(map['longitude'] as String)) : null),
|
||||||
|
);
|
||||||
|
batch.insert(
|
||||||
|
_db.remoteAssetCloudIdEntity,
|
||||||
|
companion.copyWith(assetId: Value(metadata.assetId)),
|
||||||
|
onConflict: DoUpdate((_) => companion),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error, stack) {
|
||||||
|
_logger.severe('Error: updateAssetsMetadataV1', error, stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.batch((batch) {
|
await _db.batch((batch) {
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
isFavorite: row.isFavorite,
|
isFavorite: row.isFavorite,
|
||||||
durationInSeconds: row.durationInSeconds,
|
durationInSeconds: row.durationInSeconds,
|
||||||
orientation: row.orientation,
|
orientation: row.orientation,
|
||||||
|
cloudId: row.iCloudId,
|
||||||
|
latitude: row.latitude,
|
||||||
|
longitude: row.longitude,
|
||||||
|
adjustmentTime: row.adjustmentTime,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.get();
|
.get();
|
||||||
|
|||||||
@@ -10,4 +10,8 @@ class ServerVersion extends SemVer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerVersion.fromDto(ServerVersionResponseDto dto) : super(major: dto.major, minor: dto.minor, patch: dto.patch_);
|
ServerVersion.fromDto(ServerVersionResponseDto dto) : super(major: dto.major, minor: dto.minor, patch: dto.patch_);
|
||||||
|
|
||||||
|
bool isAtLeast({int major = 0, int minor = 0, int patch = 0}) {
|
||||||
|
return this >= SemVer(major: major, minor: minor, patch: patch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
@@ -60,7 +61,8 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
(_) async {
|
(_) async {
|
||||||
try {
|
try {
|
||||||
wsProvider.connect();
|
wsProvider.connect();
|
||||||
unawaited(infoProvider.getServerInfo());
|
await infoProvider.getServerInfo();
|
||||||
|
final serverInfo = ref.read(serverInfoProvider);
|
||||||
|
|
||||||
if (Store.isBetaTimelineEnabled) {
|
if (Store.isBetaTimelineEnabled) {
|
||||||
bool syncSuccess = false;
|
bool syncSuccess = false;
|
||||||
@@ -75,6 +77,9 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
_resumeBackup(backupProvider);
|
_resumeBackup(backupProvider);
|
||||||
}),
|
}),
|
||||||
_resumeBackup(backupProvider),
|
_resumeBackup(backupProvider),
|
||||||
|
// Sync cloud IDs if server version is compatible
|
||||||
|
if (CurrentPlatform.isIOS && serverInfo.serverVersion.isAtLeast(major: 2, minor: 2))
|
||||||
|
backgroundManager.syncCloudIds(),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await backgroundManager.hashAssets();
|
await backgroundManager.hashAssets();
|
||||||
|
|||||||
28
mobile/lib/platform/native_sync_api.g.dart
generated
28
mobile/lib/platform/native_sync_api.g.dart
generated
@@ -616,4 +616,32 @@ class NativeSyncApi {
|
|||||||
return (pigeonVar_replyList[0] as Map<Object?, Object?>?)!.cast<String, List<PlatformAsset>>();
|
return (pigeonVar_replyList[0] as Map<Object?, Object?>?)!.cast<String, List<PlatformAsset>>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Map<String, String?>> getCloudIdForAssetIds(List<String> assetIds) async {
|
||||||
|
final String pigeonVar_channelName =
|
||||||
|
'dev.flutter.pigeon.immich_mobile.NativeSyncApi.getCloudIdForAssetIds$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[assetIds]);
|
||||||
|
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as Map<Object?, Object?>?)!.cast<String, String?>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ class _AssetPropertiesSectionState extends ConsumerState<_AssetPropertiesSection
|
|||||||
final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id);
|
final albums = await ref.read(assetServiceProvider).getSourceAlbums(asset.id);
|
||||||
properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', ')));
|
properties.add(_PropertyItem(label: 'Album', value: albums.map((a) => a.name).join(', ')));
|
||||||
if (CurrentPlatform.isIOS) {
|
if (CurrentPlatform.isIOS) {
|
||||||
|
properties.add(_PropertyItem(label: 'Cloud ID', value: asset.cloudId));
|
||||||
properties.add(_PropertyItem(label: 'Adjustment Time', value: asset.adjustmentTime?.toString()));
|
properties.add(_PropertyItem(label: 'Adjustment Time', value: asset.adjustmentTime?.toString()));
|
||||||
}
|
}
|
||||||
properties.add(
|
properties.add(
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Widget> slivers = [AlbumSelector(onAlbumSelected: (album) => _addCurrentAssetToAlbum(album))];
|
final List<Widget> slivers = [
|
||||||
|
const CreateAlbumButton(),
|
||||||
|
AlbumSelector(onAlbumSelected: (album) => _addCurrentAssetToAlbum(album)),
|
||||||
|
];
|
||||||
|
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -153,6 +156,9 @@ class _AddActionButtonState extends ConsumerState<AddActionButton> {
|
|||||||
context: context,
|
context: context,
|
||||||
msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
|
msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Invalidate using the asset's remote ID to refresh the "Appears in" list
|
||||||
|
ref.invalidate(albumsContainingAssetProvider(latest.remoteId!));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.mounted) {
|
if (!context.mounted) {
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.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/presentation/widgets/album/album_tile.dart';
|
import 'package:immich_mobile/presentation/widgets/album/album_tile.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/album/new_album_name_modal.widget.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.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/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.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';
|
||||||
@@ -766,3 +768,68 @@ class AddToAlbumHeader extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CreateAlbumButton extends ConsumerWidget {
|
||||||
|
const CreateAlbumButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
Future<void> onCreateAlbum() async {
|
||||||
|
var albumName = await showDialog<String?>(context: context, builder: (context) => const NewAlbumNameModal());
|
||||||
|
if (albumName == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final asset = ref.read(currentAssetNotifier);
|
||||||
|
|
||||||
|
if (asset == null) {
|
||||||
|
ImmichToast.show(context: context, msg: "Cannot load asset information.", toastType: ToastType.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final album = await ref
|
||||||
|
.read(remoteAlbumProvider.notifier)
|
||||||
|
.createAlbum(title: albumName, assetIds: [asset.remoteId!]);
|
||||||
|
|
||||||
|
if (album == null) {
|
||||||
|
ImmichToast.show(context: context, toastType: ToastType.error, msg: 'errors.failed_to_create_album'.tr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmichToast.show(
|
||||||
|
context: context,
|
||||||
|
msg: 'add_to_album_bottom_sheet_added'.tr(namedArgs: {'album': album.name}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalidate using the asset's remote ID to refresh the "Appears in" list
|
||||||
|
ref.invalidate(albumsContainingAssetProvider(asset.remoteId!));
|
||||||
|
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text("add_to_album", style: context.textTheme.titleSmall).tr(),
|
||||||
|
TextButton.icon(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
minimumSize: const Size(0, 0),
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
onPressed: onCreateAlbum,
|
||||||
|
icon: Icon(Icons.add, color: context.primaryColor),
|
||||||
|
label: Text(
|
||||||
|
"common_create_new_album",
|
||||||
|
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 14),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
|
||||||
|
class NewAlbumNameModal extends StatefulWidget {
|
||||||
|
const NewAlbumNameModal({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NewAlbumNameModal> createState() => _NewAlbumNameModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NewAlbumNameModalState extends State<NewAlbumNameModal> {
|
||||||
|
TextEditingController nameController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
nameController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text("album_name", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: nameController,
|
||||||
|
textCapitalization: TextCapitalization.words,
|
||||||
|
autofocus: true,
|
||||||
|
decoration: InputDecoration(hintText: 'name'.tr(), border: const OutlineInputBorder()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => context.pop(null),
|
||||||
|
child: Text(
|
||||||
|
"cancel",
|
||||||
|
style: TextStyle(color: Colors.red[300], fontWeight: FontWeight.bold),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.pop(nameController.text.trim());
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"create_album",
|
||||||
|
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/models/setting.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/services/setting.service.dart';
|
import 'package:immich_mobile/domain/services/setting.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/video_viewer_controls.widget.dart';
|
||||||
@@ -104,7 +105,12 @@ class NativeVideoViewer extends HookConsumerWidget {
|
|||||||
throw Exception('No file found for the video');
|
throw Exception('No file found for the video');
|
||||||
}
|
}
|
||||||
|
|
||||||
final source = await VideoSource.init(path: file.path, type: VideoSourceType.file);
|
// Pass a file:// URI so Android's Uri.parse doesn't
|
||||||
|
// interpret characters like '#' as fragment identifiers.
|
||||||
|
final source = await VideoSource.init(
|
||||||
|
path: CurrentPlatform.isAndroid ? file.uri.toString() : file.path,
|
||||||
|
type: VideoSourceType.file,
|
||||||
|
);
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
|
|
||||||
final backgroundManager = _ref.read(backgroundSyncProvider);
|
final backgroundManager = _ref.read(backgroundSyncProvider);
|
||||||
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
||||||
|
final serverInfo = _ref.read(serverInfoProvider);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
bool syncSuccess = false;
|
bool syncSuccess = false;
|
||||||
@@ -160,6 +161,9 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
_resumeBackup();
|
_resumeBackup();
|
||||||
}),
|
}),
|
||||||
_resumeBackup(),
|
_resumeBackup(),
|
||||||
|
// Sync cloud IDs if server version is compatible
|
||||||
|
if (CurrentPlatform.isIOS && serverInfo.serverVersion.isAtLeast(major: 2, minor: 2))
|
||||||
|
backgroundManager.syncCloudIds(),
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await _safeRun(backgroundManager.hashAssets(), "hashAssets");
|
await _safeRun(backgroundManager.hashAssets(), "hashAssets");
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ final backgroundSyncProvider = Provider<BackgroundSyncManager>((ref) {
|
|||||||
onHashingStart: syncStatusNotifier.startHashJob,
|
onHashingStart: syncStatusNotifier.startHashJob,
|
||||||
onHashingComplete: syncStatusNotifier.completeHashJob,
|
onHashingComplete: syncStatusNotifier.completeHashJob,
|
||||||
onHashingError: syncStatusNotifier.errorHashJob,
|
onHashingError: syncStatusNotifier.errorHashJob,
|
||||||
|
onCloudIdSyncStart: syncStatusNotifier.startCloudIdSync,
|
||||||
|
onCloudIdSyncComplete: syncStatusNotifier.completeCloudIdSync,
|
||||||
|
onCloudIdSyncError: syncStatusNotifier.errorCloudIdSync,
|
||||||
);
|
);
|
||||||
ref.onDispose(manager.cancel);
|
ref.onDispose(manager.cancel);
|
||||||
return manager;
|
return manager;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ final syncStreamRepositoryProvider = Provider((ref) => SyncStreamRepository(ref.
|
|||||||
final localSyncServiceProvider = Provider(
|
final localSyncServiceProvider = Provider(
|
||||||
(ref) => LocalSyncService(
|
(ref) => LocalSyncService(
|
||||||
localAlbumRepository: ref.watch(localAlbumRepository),
|
localAlbumRepository: ref.watch(localAlbumRepository),
|
||||||
|
localAssetRepository: ref.watch(localAssetRepository),
|
||||||
trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository),
|
trashedLocalAssetRepository: ref.watch(trashedLocalAssetRepository),
|
||||||
localFilesManager: ref.watch(localFilesManagerRepositoryProvider),
|
localFilesManager: ref.watch(localFilesManagerRepositoryProvider),
|
||||||
storageRepository: ref.watch(storageRepositoryProvider),
|
storageRepository: ref.watch(storageRepositoryProvider),
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class SyncStatusState {
|
|||||||
final SyncStatus remoteSyncStatus;
|
final SyncStatus remoteSyncStatus;
|
||||||
final SyncStatus localSyncStatus;
|
final SyncStatus localSyncStatus;
|
||||||
final SyncStatus hashJobStatus;
|
final SyncStatus hashJobStatus;
|
||||||
|
final SyncStatus cloudIdSyncStatus;
|
||||||
|
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ class SyncStatusState {
|
|||||||
this.remoteSyncStatus = SyncStatus.idle,
|
this.remoteSyncStatus = SyncStatus.idle,
|
||||||
this.localSyncStatus = SyncStatus.idle,
|
this.localSyncStatus = SyncStatus.idle,
|
||||||
this.hashJobStatus = SyncStatus.idle,
|
this.hashJobStatus = SyncStatus.idle,
|
||||||
|
this.cloudIdSyncStatus = SyncStatus.idle,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -35,12 +37,14 @@ class SyncStatusState {
|
|||||||
SyncStatus? remoteSyncStatus,
|
SyncStatus? remoteSyncStatus,
|
||||||
SyncStatus? localSyncStatus,
|
SyncStatus? localSyncStatus,
|
||||||
SyncStatus? hashJobStatus,
|
SyncStatus? hashJobStatus,
|
||||||
|
SyncStatus? cloudIdSyncStatus,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
}) {
|
}) {
|
||||||
return SyncStatusState(
|
return SyncStatusState(
|
||||||
remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus,
|
remoteSyncStatus: remoteSyncStatus ?? this.remoteSyncStatus,
|
||||||
localSyncStatus: localSyncStatus ?? this.localSyncStatus,
|
localSyncStatus: localSyncStatus ?? this.localSyncStatus,
|
||||||
hashJobStatus: hashJobStatus ?? this.hashJobStatus,
|
hashJobStatus: hashJobStatus ?? this.hashJobStatus,
|
||||||
|
cloudIdSyncStatus: cloudIdSyncStatus ?? this.cloudIdSyncStatus,
|
||||||
errorMessage: errorMessage ?? this.errorMessage,
|
errorMessage: errorMessage ?? this.errorMessage,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -48,6 +52,7 @@ class SyncStatusState {
|
|||||||
bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing;
|
bool get isRemoteSyncing => remoteSyncStatus == SyncStatus.syncing;
|
||||||
bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing;
|
bool get isLocalSyncing => localSyncStatus == SyncStatus.syncing;
|
||||||
bool get isHashing => hashJobStatus == SyncStatus.syncing;
|
bool get isHashing => hashJobStatus == SyncStatus.syncing;
|
||||||
|
bool get isCloudIdSyncing => cloudIdSyncStatus == SyncStatus.syncing;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@@ -56,11 +61,12 @@ class SyncStatusState {
|
|||||||
other.remoteSyncStatus == remoteSyncStatus &&
|
other.remoteSyncStatus == remoteSyncStatus &&
|
||||||
other.localSyncStatus == localSyncStatus &&
|
other.localSyncStatus == localSyncStatus &&
|
||||||
other.hashJobStatus == hashJobStatus &&
|
other.hashJobStatus == hashJobStatus &&
|
||||||
|
other.cloudIdSyncStatus == cloudIdSyncStatus &&
|
||||||
other.errorMessage == errorMessage;
|
other.errorMessage == errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, errorMessage);
|
int get hashCode => Object.hash(remoteSyncStatus, localSyncStatus, hashJobStatus, cloudIdSyncStatus, errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
||||||
@@ -71,6 +77,7 @@ class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
|||||||
remoteSyncStatus: SyncStatus.idle,
|
remoteSyncStatus: SyncStatus.idle,
|
||||||
localSyncStatus: SyncStatus.idle,
|
localSyncStatus: SyncStatus.idle,
|
||||||
hashJobStatus: SyncStatus.idle,
|
hashJobStatus: SyncStatus.idle,
|
||||||
|
cloudIdSyncStatus: SyncStatus.idle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +116,18 @@ class SyncStatusNotifier extends Notifier<SyncStatusState> {
|
|||||||
void startHashJob() => setHashJobStatus(SyncStatus.syncing);
|
void startHashJob() => setHashJobStatus(SyncStatus.syncing);
|
||||||
void completeHashJob() => setHashJobStatus(SyncStatus.success);
|
void completeHashJob() => setHashJobStatus(SyncStatus.success);
|
||||||
void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error);
|
void errorHashJob(String error) => setHashJobStatus(SyncStatus.error, error);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Cloud ID Sync Job
|
||||||
|
///
|
||||||
|
|
||||||
|
void setCloudIdSyncStatus(SyncStatus status, [String? errorMessage]) {
|
||||||
|
state = state.copyWith(cloudIdSyncStatus: status, errorMessage: status == SyncStatus.error ? errorMessage : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startCloudIdSync() => setCloudIdSyncStatus(SyncStatus.syncing);
|
||||||
|
void completeCloudIdSync() => setCloudIdSyncStatus(SyncStatus.success);
|
||||||
|
void errorCloudIdSync(String error) => setCloudIdSyncStatus(SyncStatus.error, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
final syncStatusProvider = NotifierProvider<SyncStatusNotifier, SyncStatusState>(SyncStatusNotifier.new);
|
final syncStatusProvider = NotifierProvider<SyncStatusNotifier, SyncStatusState>(SyncStatusNotifier.new);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:cancellation_token_http/http.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/asset_metadata.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
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/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
@@ -14,10 +15,12 @@ import 'package:immich_mobile/extensions/platform_extensions.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/backup.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/storage.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
import 'package:immich_mobile/repositories/asset_media.repository.dart';
|
||||||
import 'package:immich_mobile/repositories/upload.repository.dart';
|
import 'package:immich_mobile/repositories/upload.repository.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
@@ -34,6 +37,7 @@ final uploadServiceProvider = Provider((ref) {
|
|||||||
ref.watch(localAssetRepository),
|
ref.watch(localAssetRepository),
|
||||||
ref.watch(appSettingsServiceProvider),
|
ref.watch(appSettingsServiceProvider),
|
||||||
ref.watch(assetMediaRepositoryProvider),
|
ref.watch(assetMediaRepositoryProvider),
|
||||||
|
ref.watch(serverInfoProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
ref.onDispose(service.dispose);
|
ref.onDispose(service.dispose);
|
||||||
@@ -48,6 +52,7 @@ class UploadService {
|
|||||||
this._localAssetRepository,
|
this._localAssetRepository,
|
||||||
this._appSettingsService,
|
this._appSettingsService,
|
||||||
this._assetMediaRepository,
|
this._assetMediaRepository,
|
||||||
|
this._serverInfo,
|
||||||
) {
|
) {
|
||||||
_uploadRepository.onUploadStatus = _onUploadCallback;
|
_uploadRepository.onUploadStatus = _onUploadCallback;
|
||||||
_uploadRepository.onTaskProgress = _onTaskProgressCallback;
|
_uploadRepository.onTaskProgress = _onTaskProgressCallback;
|
||||||
@@ -59,6 +64,7 @@ class UploadService {
|
|||||||
final DriftLocalAssetRepository _localAssetRepository;
|
final DriftLocalAssetRepository _localAssetRepository;
|
||||||
final AppSettingsService _appSettingsService;
|
final AppSettingsService _appSettingsService;
|
||||||
final AssetMediaRepository _assetMediaRepository;
|
final AssetMediaRepository _assetMediaRepository;
|
||||||
|
final ServerInfo _serverInfo;
|
||||||
final Logger _logger = Logger('UploadService');
|
final Logger _logger = Logger('UploadService');
|
||||||
|
|
||||||
final StreamController<TaskStatusUpdate> _taskStatusController = StreamController<TaskStatusUpdate>.broadcast();
|
final StreamController<TaskStatusUpdate> _taskStatusController = StreamController<TaskStatusUpdate>.broadcast();
|
||||||
@@ -352,6 +358,10 @@ class UploadService {
|
|||||||
priority: priority,
|
priority: priority,
|
||||||
isFavorite: asset.isFavorite,
|
isFavorite: asset.isFavorite,
|
||||||
requiresWiFi: requiresWiFi,
|
requiresWiFi: requiresWiFi,
|
||||||
|
cloudId: asset.cloudId,
|
||||||
|
adjustmentTime: asset.adjustmentTime?.toIso8601String(),
|
||||||
|
latitude: asset.latitude?.toString(),
|
||||||
|
longitude: asset.longitude?.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,6 +393,10 @@ class UploadService {
|
|||||||
priority: 0, // Highest priority to get upload immediately
|
priority: 0, // Highest priority to get upload immediately
|
||||||
isFavorite: asset.isFavorite,
|
isFavorite: asset.isFavorite,
|
||||||
requiresWiFi: requiresWiFi,
|
requiresWiFi: requiresWiFi,
|
||||||
|
cloudId: asset.cloudId,
|
||||||
|
adjustmentTime: asset.adjustmentTime?.toIso8601String(),
|
||||||
|
latitude: asset.latitude?.toString(),
|
||||||
|
longitude: asset.longitude?.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +424,10 @@ class UploadService {
|
|||||||
int? priority,
|
int? priority,
|
||||||
bool? isFavorite,
|
bool? isFavorite,
|
||||||
bool requiresWiFi = true,
|
bool requiresWiFi = true,
|
||||||
|
String? cloudId,
|
||||||
|
String? adjustmentTime,
|
||||||
|
String? latitude,
|
||||||
|
String? longitude,
|
||||||
}) async {
|
}) async {
|
||||||
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
final serverEndpoint = Store.get(StoreKey.serverEndpoint);
|
||||||
final url = Uri.parse('$serverEndpoint/assets').toString();
|
final url = Uri.parse('$serverEndpoint/assets').toString();
|
||||||
@@ -425,6 +443,20 @@ class UploadService {
|
|||||||
'isFavorite': isFavorite?.toString() ?? 'false',
|
'isFavorite': isFavorite?.toString() ?? 'false',
|
||||||
'duration': '0',
|
'duration': '0',
|
||||||
if (fields != null) ...fields,
|
if (fields != null) ...fields,
|
||||||
|
// Include cloudId and eTag in metadata if available and server version supports it
|
||||||
|
if (CurrentPlatform.isIOS && cloudId != null && _serverInfo.serverVersion.isAtLeast(major: 2, minor: 2))
|
||||||
|
'metadata': jsonEncode([
|
||||||
|
RemoteAssetMetadataItem(
|
||||||
|
key: RemoteAssetMetadataKey.mobileApp,
|
||||||
|
value: RemoteAssetMobileAppMetadata(
|
||||||
|
cloudId: cloudId,
|
||||||
|
createdAt: createdAt.toIso8601String(),
|
||||||
|
adjustmentTime: adjustmentTime,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
return UploadTask(
|
return UploadTask(
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ class _SyncStatusIcon extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return switch (status) {
|
return switch (status) {
|
||||||
SyncStatus.idle => const Icon(Icons.pause_circle_outline_rounded),
|
SyncStatus.idle => const SizedBox.shrink(),
|
||||||
SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)),
|
SyncStatus.syncing => const SizedBox(height: 24, width: 24, child: CircularProgressIndicator(strokeWidth: 2)),
|
||||||
SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green),
|
SyncStatus.success => const Icon(Icons.check_circle_outline, color: Colors.green),
|
||||||
SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error),
|
SyncStatus.error => Icon(Icons.error_outline, color: context.colorScheme.error),
|
||||||
|
|||||||
@@ -121,4 +121,7 @@ abstract class NativeSyncApi {
|
|||||||
|
|
||||||
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
|
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
|
||||||
Map<String, List<PlatformAsset>> getTrashedAssets();
|
Map<String, List<PlatformAsset>> getTrashedAssets();
|
||||||
|
|
||||||
|
@TaskQueue(type: TaskQueueType.serialBackgroundThread)
|
||||||
|
Map<String, String?> getCloudIdForAssetIds(List<String> assetIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ void main() {
|
|||||||
registerFallbackValue(LocalAssetStub.image1);
|
registerFallbackValue(LocalAssetStub.image1);
|
||||||
registerFallbackValue(<String, String>{});
|
registerFallbackValue(<String, String>{});
|
||||||
|
|
||||||
|
when(() => mockAssetRepo.getHashMappingFromCloudId()).thenAnswer((_) async => {});
|
||||||
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
|
when(() => mockAssetRepo.updateHashes(any())).thenAnswer((_) async => {});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import 'package:immich_mobile/domain/services/store.service.dart';
|
|||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.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/local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/storage.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart';
|
||||||
@@ -25,6 +26,7 @@ import '../../repository.mocks.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
late LocalSyncService sut;
|
late LocalSyncService sut;
|
||||||
late DriftLocalAlbumRepository mockLocalAlbumRepository;
|
late DriftLocalAlbumRepository mockLocalAlbumRepository;
|
||||||
|
late DriftLocalAssetRepository mockLocalAssetRepository;
|
||||||
late DriftTrashedLocalAssetRepository mockTrashedLocalAssetRepository;
|
late DriftTrashedLocalAssetRepository mockTrashedLocalAssetRepository;
|
||||||
late LocalFilesManagerRepository mockLocalFilesManager;
|
late LocalFilesManagerRepository mockLocalFilesManager;
|
||||||
late StorageRepository mockStorageRepository;
|
late StorageRepository mockStorageRepository;
|
||||||
@@ -47,6 +49,7 @@ void main() {
|
|||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
mockLocalAlbumRepository = MockLocalAlbumRepository();
|
mockLocalAlbumRepository = MockLocalAlbumRepository();
|
||||||
|
mockLocalAssetRepository = MockLocalAssetRepository();
|
||||||
mockTrashedLocalAssetRepository = MockTrashedLocalAssetRepository();
|
mockTrashedLocalAssetRepository = MockTrashedLocalAssetRepository();
|
||||||
mockLocalFilesManager = MockLocalFilesManagerRepository();
|
mockLocalFilesManager = MockLocalFilesManagerRepository();
|
||||||
mockStorageRepository = MockStorageRepository();
|
mockStorageRepository = MockStorageRepository();
|
||||||
@@ -66,6 +69,7 @@ void main() {
|
|||||||
|
|
||||||
sut = LocalSyncService(
|
sut = LocalSyncService(
|
||||||
localAlbumRepository: mockLocalAlbumRepository,
|
localAlbumRepository: mockLocalAlbumRepository,
|
||||||
|
localAssetRepository: mockLocalAssetRepository,
|
||||||
trashedLocalAssetRepository: mockTrashedLocalAssetRepository,
|
trashedLocalAssetRepository: mockTrashedLocalAssetRepository,
|
||||||
localFilesManager: mockLocalFilesManager,
|
localFilesManager: mockLocalFilesManager,
|
||||||
storageRepository: mockStorageRepository,
|
storageRepository: mockStorageRepository,
|
||||||
|
|||||||
21
mobile/test/drift/main/generated/schema.dart
generated
21
mobile/test/drift/main/generated/schema.dart
generated
@@ -17,6 +17,7 @@ import 'schema_v11.dart' as v11;
|
|||||||
import 'schema_v12.dart' as v12;
|
import 'schema_v12.dart' as v12;
|
||||||
import 'schema_v13.dart' as v13;
|
import 'schema_v13.dart' as v13;
|
||||||
import 'schema_v14.dart' as v14;
|
import 'schema_v14.dart' as v14;
|
||||||
|
import 'schema_v15.dart' as v15;
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
@@ -50,10 +51,28 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||||||
return v13.DatabaseAtV13(db);
|
return v13.DatabaseAtV13(db);
|
||||||
case 14:
|
case 14:
|
||||||
return v14.DatabaseAtV14(db);
|
return v14.DatabaseAtV14(db);
|
||||||
|
case 15:
|
||||||
|
return v15.DatabaseAtV15(db);
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
|
static const versions = const [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
8265
mobile/test/drift/main/generated/schema_v15.dart
generated
Normal file
8265
mobile/test/drift/main/generated/schema_v15.dart
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,22 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:drift/drift.dart' hide isNull, isNotNull;
|
import 'package:drift/drift.dart' hide isNull, isNotNull;
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
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/services/store.service.dart';
|
import 'package:immich_mobile/domain/services/store.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/store.repository.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_config.model.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_disk_info.model.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_features.model.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
|
import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/services/upload.service.dart';
|
import 'package:immich_mobile/services/upload.service.dart';
|
||||||
import 'package:mocktail/mocktail.dart';
|
import 'package:mocktail/mocktail.dart';
|
||||||
@@ -16,8 +24,29 @@ import 'package:mocktail/mocktail.dart';
|
|||||||
import '../domain/service.mock.dart';
|
import '../domain/service.mock.dart';
|
||||||
import '../fixtures/asset.stub.dart';
|
import '../fixtures/asset.stub.dart';
|
||||||
import '../infrastructure/repository.mock.dart';
|
import '../infrastructure/repository.mock.dart';
|
||||||
import '../repository.mocks.dart';
|
|
||||||
import '../mocks/asset_entity.mock.dart';
|
import '../mocks/asset_entity.mock.dart';
|
||||||
|
import '../repository.mocks.dart';
|
||||||
|
|
||||||
|
// Test ServerInfo stub
|
||||||
|
const _serverInfo = ServerInfo(
|
||||||
|
serverVersion: ServerVersion(major: 2, minor: 4, patch: 0),
|
||||||
|
latestVersion: ServerVersion(major: 2, minor: 4, patch: 0),
|
||||||
|
serverFeatures: ServerFeatures(trash: true, map: true, oauthEnabled: false, passwordLogin: true, ocr: false),
|
||||||
|
serverConfig: ServerConfig(
|
||||||
|
trashDays: 30,
|
||||||
|
oauthButtonText: 'Login with OAuth',
|
||||||
|
externalDomain: '',
|
||||||
|
mapDarkStyleUrl: '',
|
||||||
|
mapLightStyleUrl: '',
|
||||||
|
),
|
||||||
|
serverDiskInfo: ServerDiskInfo(
|
||||||
|
diskAvailable: '100GB',
|
||||||
|
diskSize: '500GB',
|
||||||
|
diskUse: '400GB',
|
||||||
|
diskUsagePercentage: 80.0,
|
||||||
|
),
|
||||||
|
versionStatus: VersionStatus.upToDate,
|
||||||
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late UploadService sut;
|
late UploadService sut;
|
||||||
@@ -62,6 +91,7 @@ void main() {
|
|||||||
mockLocalAssetRepository,
|
mockLocalAssetRepository,
|
||||||
mockAppSettingsService,
|
mockAppSettingsService,
|
||||||
mockAssetMediaRepository,
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
mockUploadRepository.onUploadStatus = (_) {};
|
mockUploadRepository.onUploadStatus = (_) {};
|
||||||
@@ -165,4 +195,223 @@ void main() {
|
|||||||
verify(() => mockAssetMediaRepository.getOriginalFilename(asset.id)).called(1);
|
verify(() => mockAssetMediaRepository.getOriginalFilename(asset.id)).called(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Server Info - cloudId and eTag metadata', () {
|
||||||
|
test('should include cloudId and eTag metadata on iOS when server version is 2.4+', () async {
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||||
|
|
||||||
|
final sutWithV24 = UploadService(
|
||||||
|
mockUploadRepository,
|
||||||
|
mockBackupRepository,
|
||||||
|
mockStorageRepository,
|
||||||
|
mockLocalAssetRepository,
|
||||||
|
mockAppSettingsService,
|
||||||
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo,
|
||||||
|
);
|
||||||
|
addTearDown(() => sutWithV24.dispose());
|
||||||
|
|
||||||
|
final assetWithCloudId = LocalAsset(
|
||||||
|
id: 'test-asset-id',
|
||||||
|
name: 'test.jpg',
|
||||||
|
type: AssetType.image,
|
||||||
|
createdAt: DateTime(2025, 1, 1),
|
||||||
|
updatedAt: DateTime(2025, 1, 2),
|
||||||
|
cloudId: 'cloud-id-123',
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: -122.4194,
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockEntity = MockAssetEntity();
|
||||||
|
final mockFile = File('/path/to/test.jpg');
|
||||||
|
|
||||||
|
when(() => mockEntity.isLivePhoto).thenReturn(false);
|
||||||
|
when(() => mockStorageRepository.getAssetEntityForAsset(assetWithCloudId)).thenAnswer((_) async => mockEntity);
|
||||||
|
when(() => mockStorageRepository.getFileForAsset(assetWithCloudId.id)).thenAnswer((_) async => mockFile);
|
||||||
|
when(() => mockAssetMediaRepository.getOriginalFilename(assetWithCloudId.id)).thenAnswer((_) async => 'test.jpg');
|
||||||
|
|
||||||
|
final task = await sutWithV24.getUploadTask(assetWithCloudId);
|
||||||
|
|
||||||
|
expect(task, isNotNull);
|
||||||
|
expect(task!.fields.containsKey('metadata'), isTrue);
|
||||||
|
|
||||||
|
final metadata = jsonDecode(task.fields['metadata']!) as List;
|
||||||
|
expect(metadata, hasLength(1));
|
||||||
|
expect(metadata[0]['key'], equals('mobile-app'));
|
||||||
|
expect(metadata[0]['value']['iCloudId'], equals('cloud-id-123'));
|
||||||
|
expect(metadata[0]['value']['eTag'], isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should NOT include metadata on iOS when server version is below 2.4', () async {
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||||
|
|
||||||
|
final sutWithV23 = UploadService(
|
||||||
|
mockUploadRepository,
|
||||||
|
mockBackupRepository,
|
||||||
|
mockStorageRepository,
|
||||||
|
mockLocalAssetRepository,
|
||||||
|
mockAppSettingsService,
|
||||||
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo.copyWith(
|
||||||
|
serverVersion: const ServerVersion(major: 2, minor: 3, patch: 0),
|
||||||
|
latestVersion: const ServerVersion(major: 2, minor: 3, patch: 0),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
addTearDown(() => sutWithV23.dispose());
|
||||||
|
|
||||||
|
final assetWithCloudId = LocalAsset(
|
||||||
|
id: 'test-asset-id',
|
||||||
|
name: 'test.jpg',
|
||||||
|
type: AssetType.image,
|
||||||
|
createdAt: DateTime(2025, 1, 1),
|
||||||
|
updatedAt: DateTime(2025, 1, 2),
|
||||||
|
cloudId: 'cloud-id-123',
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: -122.4194,
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockEntity = MockAssetEntity();
|
||||||
|
final mockFile = File('/path/to/test.jpg');
|
||||||
|
|
||||||
|
when(() => mockEntity.isLivePhoto).thenReturn(false);
|
||||||
|
when(() => mockStorageRepository.getAssetEntityForAsset(assetWithCloudId)).thenAnswer((_) async => mockEntity);
|
||||||
|
when(() => mockStorageRepository.getFileForAsset(assetWithCloudId.id)).thenAnswer((_) async => mockFile);
|
||||||
|
when(() => mockAssetMediaRepository.getOriginalFilename(assetWithCloudId.id)).thenAnswer((_) async => 'test.jpg');
|
||||||
|
|
||||||
|
final task = await sutWithV23.getUploadTask(assetWithCloudId);
|
||||||
|
|
||||||
|
expect(task, isNotNull);
|
||||||
|
expect(task!.fields.containsKey('metadata'), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should NOT include metadata on Android regardless of server version', () async {
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.android;
|
||||||
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||||
|
|
||||||
|
final sutAndroid = UploadService(
|
||||||
|
mockUploadRepository,
|
||||||
|
mockBackupRepository,
|
||||||
|
mockStorageRepository,
|
||||||
|
mockLocalAssetRepository,
|
||||||
|
mockAppSettingsService,
|
||||||
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo,
|
||||||
|
);
|
||||||
|
addTearDown(() => sutAndroid.dispose());
|
||||||
|
|
||||||
|
final assetWithCloudId = LocalAsset(
|
||||||
|
id: 'test-asset-id',
|
||||||
|
name: 'test.jpg',
|
||||||
|
type: AssetType.image,
|
||||||
|
createdAt: DateTime(2025, 1, 1),
|
||||||
|
updatedAt: DateTime(2025, 1, 2),
|
||||||
|
cloudId: 'cloud-id-123',
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: -122.4194,
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockEntity = MockAssetEntity();
|
||||||
|
final mockFile = File('/path/to/test.jpg');
|
||||||
|
|
||||||
|
when(() => mockEntity.isLivePhoto).thenReturn(false);
|
||||||
|
when(() => mockStorageRepository.getAssetEntityForAsset(assetWithCloudId)).thenAnswer((_) async => mockEntity);
|
||||||
|
when(() => mockStorageRepository.getFileForAsset(assetWithCloudId.id)).thenAnswer((_) async => mockFile);
|
||||||
|
when(() => mockAssetMediaRepository.getOriginalFilename(assetWithCloudId.id)).thenAnswer((_) async => 'test.jpg');
|
||||||
|
|
||||||
|
final task = await sutAndroid.getUploadTask(assetWithCloudId);
|
||||||
|
|
||||||
|
expect(task, isNotNull);
|
||||||
|
expect(task!.fields.containsKey('metadata'), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should NOT include metadata when cloudId is null even on iOS with server 2.4+', () async {
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||||
|
|
||||||
|
final sutWithV24 = UploadService(
|
||||||
|
mockUploadRepository,
|
||||||
|
mockBackupRepository,
|
||||||
|
mockStorageRepository,
|
||||||
|
mockLocalAssetRepository,
|
||||||
|
mockAppSettingsService,
|
||||||
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo,
|
||||||
|
);
|
||||||
|
addTearDown(() => sutWithV24.dispose());
|
||||||
|
|
||||||
|
final assetWithoutCloudId = LocalAsset(
|
||||||
|
id: 'test-asset-id',
|
||||||
|
name: 'test.jpg',
|
||||||
|
type: AssetType.image,
|
||||||
|
createdAt: DateTime(2025, 1, 1),
|
||||||
|
updatedAt: DateTime(2025, 1, 2),
|
||||||
|
cloudId: null, // No cloudId
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockEntity = MockAssetEntity();
|
||||||
|
final mockFile = File('/path/to/test.jpg');
|
||||||
|
|
||||||
|
when(() => mockEntity.isLivePhoto).thenReturn(false);
|
||||||
|
when(() => mockStorageRepository.getAssetEntityForAsset(assetWithoutCloudId)).thenAnswer((_) async => mockEntity);
|
||||||
|
when(() => mockStorageRepository.getFileForAsset(assetWithoutCloudId.id)).thenAnswer((_) async => mockFile);
|
||||||
|
when(
|
||||||
|
() => mockAssetMediaRepository.getOriginalFilename(assetWithoutCloudId.id),
|
||||||
|
).thenAnswer((_) async => 'test.jpg');
|
||||||
|
|
||||||
|
final task = await sutWithV24.getUploadTask(assetWithoutCloudId);
|
||||||
|
|
||||||
|
expect(task, isNotNull);
|
||||||
|
expect(task!.fields.containsKey('metadata'), isFalse);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should include metadata for live photos with cloudId on iOS 2.4+', () async {
|
||||||
|
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||||
|
addTearDown(() => debugDefaultTargetPlatformOverride = null);
|
||||||
|
|
||||||
|
final sutWithV24 = UploadService(
|
||||||
|
mockUploadRepository,
|
||||||
|
mockBackupRepository,
|
||||||
|
mockStorageRepository,
|
||||||
|
mockLocalAssetRepository,
|
||||||
|
mockAppSettingsService,
|
||||||
|
mockAssetMediaRepository,
|
||||||
|
_serverInfo,
|
||||||
|
);
|
||||||
|
addTearDown(() => sutWithV24.dispose());
|
||||||
|
|
||||||
|
final assetWithCloudId = LocalAsset(
|
||||||
|
id: 'test-livephoto-id',
|
||||||
|
name: 'livephoto.heic',
|
||||||
|
type: AssetType.image,
|
||||||
|
createdAt: DateTime(2025, 1, 1),
|
||||||
|
updatedAt: DateTime(2025, 1, 2),
|
||||||
|
cloudId: 'cloud-id-livephoto',
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: -122.4194,
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockEntity = MockAssetEntity();
|
||||||
|
final mockFile = File('/path/to/livephoto.heic');
|
||||||
|
|
||||||
|
when(() => mockEntity.isLivePhoto).thenReturn(true);
|
||||||
|
when(() => mockStorageRepository.getAssetEntityForAsset(assetWithCloudId)).thenAnswer((_) async => mockEntity);
|
||||||
|
when(() => mockStorageRepository.getFileForAsset(assetWithCloudId.id)).thenAnswer((_) async => mockFile);
|
||||||
|
when(
|
||||||
|
() => mockAssetMediaRepository.getOriginalFilename(assetWithCloudId.id),
|
||||||
|
).thenAnswer((_) async => 'livephoto.heic');
|
||||||
|
|
||||||
|
final task = await sutWithV24.getLivePhotoUploadTask(assetWithCloudId, 'video-123');
|
||||||
|
|
||||||
|
expect(task, isNotNull);
|
||||||
|
expect(task!.fields.containsKey('metadata'), isTrue);
|
||||||
|
expect(task.fields['livePhotoVideoId'], equals('video-123'));
|
||||||
|
|
||||||
|
final metadata = jsonDecode(task.fields['metadata']!) as List;
|
||||||
|
expect(metadata, hasLength(1));
|
||||||
|
expect(metadata[0]['key'], equals('mobile-app'));
|
||||||
|
expect(metadata[0]['value']['iCloudId'], equals('cloud-id-livephoto'));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
214
plugins/package-lock.json
generated
214
plugins/package-lock.json
generated
@@ -15,9 +15,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.1.tgz",
|
||||||
"integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
|
"integrity": "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.1.tgz",
|
||||||
"integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
|
"integrity": "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-arm64": {
|
"node_modules/@esbuild/android-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
|
"integrity": "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -66,9 +66,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/android-x64": {
|
"node_modules/@esbuild/android-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
|
"integrity": "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -83,9 +83,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
"node_modules/@esbuild/darwin-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
|
"integrity": "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -100,9 +100,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
|
"integrity": "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -117,9 +117,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
"node_modules/@esbuild/freebsd-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
|
"integrity": "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -134,9 +134,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
"node_modules/@esbuild/freebsd-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
|
"integrity": "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -151,9 +151,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm": {
|
"node_modules/@esbuild/linux-arm": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.1.tgz",
|
||||||
"integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
|
"integrity": "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -168,9 +168,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
"node_modules/@esbuild/linux-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
|
"integrity": "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -185,9 +185,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
"node_modules/@esbuild/linux-ia32": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.1.tgz",
|
||||||
"integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
|
"integrity": "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -202,9 +202,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
"node_modules/@esbuild/linux-loong64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.1.tgz",
|
||||||
"integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
|
"integrity": "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -219,9 +219,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
"node_modules/@esbuild/linux-mips64el": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.1.tgz",
|
||||||
"integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
|
"integrity": "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"mips64el"
|
"mips64el"
|
||||||
],
|
],
|
||||||
@@ -236,9 +236,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
"node_modules/@esbuild/linux-ppc64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.1.tgz",
|
||||||
"integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
|
"integrity": "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -253,9 +253,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
"node_modules/@esbuild/linux-riscv64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.1.tgz",
|
||||||
"integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
|
"integrity": "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -270,9 +270,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
"node_modules/@esbuild/linux-s390x": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.1.tgz",
|
||||||
"integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
|
"integrity": "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -287,9 +287,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/linux-x64": {
|
"node_modules/@esbuild/linux-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
|
"integrity": "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -304,9 +304,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
"node_modules/@esbuild/netbsd-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
|
"integrity": "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -321,9 +321,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
"node_modules/@esbuild/netbsd-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
|
"integrity": "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -338,9 +338,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
"node_modules/@esbuild/openbsd-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
|
"integrity": "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -355,9 +355,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
"node_modules/@esbuild/openbsd-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
|
"integrity": "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -372,9 +372,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
"node_modules/@esbuild/openharmony-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
|
"integrity": "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -389,9 +389,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
"node_modules/@esbuild/sunos-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
|
"integrity": "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -406,9 +406,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
"node_modules/@esbuild/win32-arm64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.1.tgz",
|
||||||
"integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
|
"integrity": "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -423,9 +423,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
"node_modules/@esbuild/win32-ia32": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.1.tgz",
|
||||||
"integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
|
"integrity": "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -440,9 +440,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/win32-x64": {
|
"node_modules/@esbuild/win32-x64": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.1.tgz",
|
||||||
"integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
|
"integrity": "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -467,9 +467,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/esbuild": {
|
"node_modules/esbuild": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.1",
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz",
|
||||||
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
|
"integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -480,32 +480,32 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@esbuild/aix-ppc64": "0.27.0",
|
"@esbuild/aix-ppc64": "0.27.1",
|
||||||
"@esbuild/android-arm": "0.27.0",
|
"@esbuild/android-arm": "0.27.1",
|
||||||
"@esbuild/android-arm64": "0.27.0",
|
"@esbuild/android-arm64": "0.27.1",
|
||||||
"@esbuild/android-x64": "0.27.0",
|
"@esbuild/android-x64": "0.27.1",
|
||||||
"@esbuild/darwin-arm64": "0.27.0",
|
"@esbuild/darwin-arm64": "0.27.1",
|
||||||
"@esbuild/darwin-x64": "0.27.0",
|
"@esbuild/darwin-x64": "0.27.1",
|
||||||
"@esbuild/freebsd-arm64": "0.27.0",
|
"@esbuild/freebsd-arm64": "0.27.1",
|
||||||
"@esbuild/freebsd-x64": "0.27.0",
|
"@esbuild/freebsd-x64": "0.27.1",
|
||||||
"@esbuild/linux-arm": "0.27.0",
|
"@esbuild/linux-arm": "0.27.1",
|
||||||
"@esbuild/linux-arm64": "0.27.0",
|
"@esbuild/linux-arm64": "0.27.1",
|
||||||
"@esbuild/linux-ia32": "0.27.0",
|
"@esbuild/linux-ia32": "0.27.1",
|
||||||
"@esbuild/linux-loong64": "0.27.0",
|
"@esbuild/linux-loong64": "0.27.1",
|
||||||
"@esbuild/linux-mips64el": "0.27.0",
|
"@esbuild/linux-mips64el": "0.27.1",
|
||||||
"@esbuild/linux-ppc64": "0.27.0",
|
"@esbuild/linux-ppc64": "0.27.1",
|
||||||
"@esbuild/linux-riscv64": "0.27.0",
|
"@esbuild/linux-riscv64": "0.27.1",
|
||||||
"@esbuild/linux-s390x": "0.27.0",
|
"@esbuild/linux-s390x": "0.27.1",
|
||||||
"@esbuild/linux-x64": "0.27.0",
|
"@esbuild/linux-x64": "0.27.1",
|
||||||
"@esbuild/netbsd-arm64": "0.27.0",
|
"@esbuild/netbsd-arm64": "0.27.1",
|
||||||
"@esbuild/netbsd-x64": "0.27.0",
|
"@esbuild/netbsd-x64": "0.27.1",
|
||||||
"@esbuild/openbsd-arm64": "0.27.0",
|
"@esbuild/openbsd-arm64": "0.27.1",
|
||||||
"@esbuild/openbsd-x64": "0.27.0",
|
"@esbuild/openbsd-x64": "0.27.1",
|
||||||
"@esbuild/openharmony-arm64": "0.27.0",
|
"@esbuild/openharmony-arm64": "0.27.1",
|
||||||
"@esbuild/sunos-x64": "0.27.0",
|
"@esbuild/sunos-x64": "0.27.1",
|
||||||
"@esbuild/win32-arm64": "0.27.0",
|
"@esbuild/win32-arm64": "0.27.1",
|
||||||
"@esbuild/win32-ia32": "0.27.0",
|
"@esbuild/win32-ia32": "0.27.1",
|
||||||
"@esbuild/win32-x64": "0.27.0"
|
"@esbuild/win32-x64": "0.27.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
|
|||||||
1233
pnpm-lock.yaml
generated
1233
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -89,6 +89,7 @@ export class MetadataRepository {
|
|||||||
// Enable exiftool LFS to parse metadata for files larger than 2GB.
|
// Enable exiftool LFS to parse metadata for files larger than 2GB.
|
||||||
readArgs: ['-api', 'largefilesupport=1'],
|
readArgs: ['-api', 'largefilesupport=1'],
|
||||||
writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'],
|
writeArgs: ['-api', 'largefilesupport=1', '-overwrite_original'],
|
||||||
|
taskTimeoutMillis: 2 * 60 * 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
constructor(private logger: LoggingRepository) {
|
constructor(private logger: LoggingRepository) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"build:stats": "BUILD_STATS=true vite build",
|
"build:stats": "BUILD_STATS=true vite build",
|
||||||
"package": "svelte-kit package",
|
"package": "svelte-kit package",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings",
|
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'state_referenced_locally:ignore'",
|
||||||
"check:typescript": "tsc --noEmit",
|
"check:typescript": "tsc --noEmit",
|
||||||
"check:watch": "pnpm run check:svelte --watch",
|
"check:watch": "pnpm run check:svelte --watch",
|
||||||
"check:code": "pnpm run format && pnpm run lint && pnpm run check:svelte && pnpm run check:typescript",
|
"check:code": "pnpm run format && pnpm run lint && pnpm run check:svelte && pnpm run check:typescript",
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
"prettier-plugin-sort-json": "^4.1.1",
|
"prettier-plugin-sort-json": "^4.1.1",
|
||||||
"prettier-plugin-svelte": "^3.3.3",
|
"prettier-plugin-svelte": "^3.3.3",
|
||||||
"rollup-plugin-visualizer": "^6.0.0",
|
"rollup-plugin-visualizer": "^6.0.0",
|
||||||
"svelte": "5.43.3",
|
"svelte": "5.45.5",
|
||||||
"svelte-check": "^4.1.5",
|
"svelte-check": "^4.1.5",
|
||||||
"svelte-eslint-parser": "^1.3.3",
|
"svelte-eslint-parser": "^1.3.3",
|
||||||
"tailwindcss": "^4.1.7",
|
"tailwindcss": "^4.1.7",
|
||||||
|
|||||||
@@ -114,7 +114,11 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await modalManager.show(AssetChangeDateModal, { asset: toTimelineAsset(asset), initialDate: dateTime });
|
await modalManager.show(AssetChangeDateModal, {
|
||||||
|
asset: toTimelineAsset(asset),
|
||||||
|
initialDate: dateTime,
|
||||||
|
initialTimeZone: timeZone,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@
|
|||||||
// the performance benefits of deferred layouts while still supporting deep linking
|
// the performance benefits of deferred layouts while still supporting deep linking
|
||||||
// to assets at the end of the timeline.
|
// to assets at the end of the timeline.
|
||||||
timelineManager.isScrollingOnLoad = true;
|
timelineManager.isScrollingOnLoad = true;
|
||||||
const monthGroup = await timelineManager.findMonthGroupForAsset(assetId);
|
const monthGroup = await timelineManager.findMonthGroupForAsset({ id: assetId });
|
||||||
if (!monthGroup) {
|
if (!monthGroup) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util';
|
import { plainDateTimeCompare, type TimelineYearMonth } from '$lib/utils/timeline-util';
|
||||||
import { AssetOrder } from '@immich/sdk';
|
import { AssetOrder, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { MonthGroup } from '../month-group.svelte';
|
import type { MonthGroup } from '../month-group.svelte';
|
||||||
import { TimelineManager } from '../timeline-manager.svelte';
|
import { TimelineManager } from '../timeline-manager.svelte';
|
||||||
@@ -7,12 +7,16 @@ import type { AssetDescriptor, Direction, TimelineAsset } from '../types';
|
|||||||
|
|
||||||
export async function getAssetWithOffset(
|
export async function getAssetWithOffset(
|
||||||
timelineManager: TimelineManager,
|
timelineManager: TimelineManager,
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
const { asset, monthGroup } = findMonthGroupForAsset(timelineManager, assetDescriptor.id) ?? {};
|
const monthGroup = await timelineManager.findMonthGroupForAsset(assetDescriptor);
|
||||||
if (!monthGroup || !asset) {
|
if (!monthGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const asset = monthGroup.findAssetById(assetDescriptor);
|
||||||
|
if (!asset) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -524,6 +524,7 @@ describe('TimelineManager', () => {
|
|||||||
{ count: 3, timeBucket: '2024-01-01T00:00:00.000Z' },
|
{ count: 3, timeBucket: '2024-01-01T00:00:00.000Z' },
|
||||||
]);
|
]);
|
||||||
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
|
sdkMock.getTimeBucket.mockImplementation(({ timeBucket }) => Promise.resolve(bucketAssetsResponse[timeBucket]));
|
||||||
|
sdkMock.getAssetInfo.mockRejectedValue(new Error('Asset not found'));
|
||||||
await timelineManager.updateViewport({ width: 1588, height: 1000 });
|
await timelineManager.updateViewport({ width: 1588, height: 1000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ import { WebsocketSupport } from '$lib/managers/timeline-manager/internal/websoc
|
|||||||
import { CancellableTask } from '$lib/utils/cancellable-task';
|
import { CancellableTask } from '$lib/utils/cancellable-task';
|
||||||
import { PersistedLocalStorage } from '$lib/utils/persisted';
|
import { PersistedLocalStorage } from '$lib/utils/persisted';
|
||||||
import {
|
import {
|
||||||
|
isAssetResponseDto,
|
||||||
setDifference,
|
setDifference,
|
||||||
toTimelineAsset,
|
toTimelineAsset,
|
||||||
type TimelineDateTime,
|
type TimelineDateTime,
|
||||||
type TimelineYearMonth,
|
type TimelineYearMonth,
|
||||||
} from '$lib/utils/timeline-util';
|
} from '$lib/utils/timeline-util';
|
||||||
import { AssetOrder, getAssetInfo, getTimeBuckets } from '@immich/sdk';
|
import { AssetOrder, getAssetInfo, getTimeBuckets, type AssetResponseDto } from '@immich/sdk';
|
||||||
import { clamp, isEqual } from 'lodash-es';
|
import { clamp, isEqual } from 'lodash-es';
|
||||||
import { SvelteDate, SvelteSet } from 'svelte/reactivity';
|
import { SvelteDate, SvelteSet } from 'svelte/reactivity';
|
||||||
import { DayGroup } from './day-group.svelte';
|
import { DayGroup } from './day-group.svelte';
|
||||||
@@ -343,27 +344,30 @@ export class TimelineManager extends VirtualScrollManager {
|
|||||||
this.addAssetsUpsertSegments([...notExcluded]);
|
this.addAssetsUpsertSegments([...notExcluded]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findMonthGroupForAsset(id: string) {
|
async findMonthGroupForAsset(asset: AssetDescriptor | AssetResponseDto) {
|
||||||
if (!this.isInitialized) {
|
if (!this.isInitialized) {
|
||||||
await this.initTask.waitUntilCompletion();
|
await this.initTask.waitUntilCompletion();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { id } = asset;
|
||||||
let { monthGroup } = findMonthGroupForAssetUtil(this, id) ?? {};
|
let { monthGroup } = findMonthGroupForAssetUtil(this, id) ?? {};
|
||||||
if (monthGroup) {
|
if (monthGroup) {
|
||||||
return monthGroup;
|
return monthGroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await getAssetInfo({ ...authManager.params, id }).catch(() => null);
|
const response = isAssetResponseDto(asset)
|
||||||
|
? asset
|
||||||
|
: await getAssetInfo({ ...authManager.params, id }).catch(() => null);
|
||||||
if (!response) {
|
if (!response) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const asset = toTimelineAsset(response);
|
const timelineAsset = toTimelineAsset(response);
|
||||||
if (!asset || this.isExcluded(asset)) {
|
if (this.isExcluded(timelineAsset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
monthGroup = await this.#loadMonthGroupAtTime(asset.localDateTime, { cancelable: false });
|
monthGroup = await this.#loadMonthGroupAtTime(timelineAsset.localDateTime, { cancelable: false });
|
||||||
if (monthGroup?.findAssetById({ id })) {
|
if (monthGroup?.findAssetById({ id })) {
|
||||||
return monthGroup;
|
return monthGroup;
|
||||||
}
|
}
|
||||||
@@ -532,14 +536,14 @@ export class TimelineManager extends VirtualScrollManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getLaterAsset(
|
async getLaterAsset(
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'later');
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEarlierAsset(
|
async getEarlierAsset(
|
||||||
assetDescriptor: AssetDescriptor,
|
assetDescriptor: AssetDescriptor | AssetResponseDto,
|
||||||
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
interval: 'asset' | 'day' | 'month' | 'year' = 'asset',
|
||||||
): Promise<TimelineAsset | undefined> {
|
): Promise<TimelineAsset | undefined> {
|
||||||
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
return await getAssetWithOffset(this, assetDescriptor, interval, 'earlier');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
import type { AssetDescriptor, TimelineAsset, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
|
||||||
import { locale } from '$lib/stores/preferences.store';
|
import { locale } from '$lib/stores/preferences.store';
|
||||||
import { getAssetRatio } from '$lib/utils/asset-utils';
|
import { getAssetRatio } from '$lib/utils/asset-utils';
|
||||||
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
import { AssetTypeEnum, type AssetResponseDto } from '@immich/sdk';
|
||||||
@@ -192,8 +192,13 @@ export const toTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset):
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isTimelineAsset = (unknownAsset: AssetResponseDto | TimelineAsset): unknownAsset is TimelineAsset =>
|
export const isTimelineAsset = (
|
||||||
(unknownAsset as TimelineAsset).ratio !== undefined;
|
unknownAsset: AssetDescriptor | AssetResponseDto | TimelineAsset,
|
||||||
|
): unknownAsset is TimelineAsset => (unknownAsset as TimelineAsset).ratio !== undefined;
|
||||||
|
|
||||||
|
export const isAssetResponseDto = (
|
||||||
|
unknownAsset: AssetDescriptor | AssetResponseDto | TimelineAsset,
|
||||||
|
): unknownAsset is AssetResponseDto => (unknownAsset as AssetResponseDto).type !== undefined;
|
||||||
|
|
||||||
export const isTimelineAssets = (assets: AssetResponseDto[] | TimelineAsset[]): assets is TimelineAsset[] =>
|
export const isTimelineAssets = (assets: AssetResponseDto[] | TimelineAsset[]): assets is TimelineAsset[] =>
|
||||||
assets.length === 0 || 'ratio' in assets[0];
|
assets.length === 0 || 'ratio' in assets[0];
|
||||||
|
|||||||
@@ -15,9 +15,24 @@
|
|||||||
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
|
import { joinPaths, TreeNode } from '$lib/utils/tree-utils';
|
||||||
import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk';
|
import { deleteTag, getAllTags, type TagResponseDto } from '@immich/sdk';
|
||||||
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
import { Button, HStack, modalManager, Text } from '@immich/ui';
|
||||||
import { mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js';
|
import { mdiDotsVertical, mdiPencil, mdiPlus, mdiTag, mdiTagMultiple, mdiTrashCanOutline } from '@mdi/js';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
|
import AssetSelectControlBar from '$lib/components/timeline/AssetSelectControlBar.svelte';
|
||||||
|
import AddToAlbum from '$lib/components/timeline/actions/AddToAlbumAction.svelte';
|
||||||
|
import ArchiveAction from '$lib/components/timeline/actions/ArchiveAction.svelte';
|
||||||
|
import ChangeDate from '$lib/components/timeline/actions/ChangeDateAction.svelte';
|
||||||
|
import ChangeDescription from '$lib/components/timeline/actions/ChangeDescriptionAction.svelte';
|
||||||
|
import ChangeLocation from '$lib/components/timeline/actions/ChangeLocationAction.svelte';
|
||||||
|
import CreateSharedLink from '$lib/components/timeline/actions/CreateSharedLinkAction.svelte';
|
||||||
|
import DeleteAssets from '$lib/components/timeline/actions/DeleteAssetsAction.svelte';
|
||||||
|
import DownloadAction from '$lib/components/timeline/actions/DownloadAction.svelte';
|
||||||
|
import FavoriteAction from '$lib/components/timeline/actions/FavoriteAction.svelte';
|
||||||
|
import SelectAllAssets from '$lib/components/timeline/actions/SelectAllAction.svelte';
|
||||||
|
import SetVisibilityAction from '$lib/components/timeline/actions/SetVisibilityAction.svelte';
|
||||||
|
import TagAction from '$lib/components/timeline/actions/TagAction.svelte';
|
||||||
|
import ButtonContextMenu from '$lib/components/shared-components/context-menu/button-context-menu.svelte';
|
||||||
|
import { preferences, user } from '$lib/stores/user.store';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: PageData;
|
data: PageData;
|
||||||
@@ -79,6 +94,11 @@
|
|||||||
// navigate to parent
|
// navigate to parent
|
||||||
await navigateToView(tag.parent ? tag.parent.path : '');
|
await navigateToView(tag.parent ? tag.parent.path : '');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSetVisibility = (assetIds: string[]) => {
|
||||||
|
timelineManager.removeAssets(assetIds);
|
||||||
|
assetInteraction.clearMultiselect();
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserPageLayout title={data.meta.title}>
|
<UserPageLayout title={data.meta.title}>
|
||||||
@@ -131,3 +151,45 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
</UserPageLayout>
|
</UserPageLayout>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
{#if assetInteraction.selectionActive}
|
||||||
|
<div class="fixed top-0 start-0 w-full">
|
||||||
|
<AssetSelectControlBar
|
||||||
|
ownerId={$user.id}
|
||||||
|
assets={assetInteraction.selectedAssets}
|
||||||
|
clearSelect={() => assetInteraction.clearMultiselect()}
|
||||||
|
>
|
||||||
|
<CreateSharedLink />
|
||||||
|
<SelectAllAssets {timelineManager} {assetInteraction} />
|
||||||
|
<ButtonContextMenu icon={mdiPlus} title={$t('add_to')}>
|
||||||
|
<AddToAlbum />
|
||||||
|
<AddToAlbum shared />
|
||||||
|
</ButtonContextMenu>
|
||||||
|
<FavoriteAction
|
||||||
|
removeFavorite={assetInteraction.isAllFavorite}
|
||||||
|
onFavorite={(ids, isFavorite) => timelineManager.update(ids, (asset) => (asset.isFavorite = isFavorite))}
|
||||||
|
></FavoriteAction>
|
||||||
|
<ButtonContextMenu icon={mdiDotsVertical} title={$t('menu')}>
|
||||||
|
<DownloadAction menuItem />
|
||||||
|
<ChangeDate menuItem />
|
||||||
|
<ChangeDescription menuItem />
|
||||||
|
<ChangeLocation menuItem />
|
||||||
|
<ArchiveAction
|
||||||
|
menuItem
|
||||||
|
onArchive={(ids, visibility) => timelineManager.update(ids, (asset) => (asset.visibility = visibility))}
|
||||||
|
/>
|
||||||
|
{#if $preferences.tags.enabled}
|
||||||
|
<TagAction menuItem />
|
||||||
|
{/if}
|
||||||
|
<DeleteAssets
|
||||||
|
menuItem
|
||||||
|
onAssetDelete={(assetIds) => timelineManager.removeAssets(assetIds)}
|
||||||
|
onUndoDelete={(assets) => timelineManager.upsertAssets(assets)}
|
||||||
|
/>
|
||||||
|
<SetVisibilityAction menuItem onVisibilitySet={handleSetVisibility} />
|
||||||
|
</ButtonContextMenu>
|
||||||
|
</AssetSelectControlBar>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user