Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4202dd6c9a |
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"name": "github",
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write ."
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prettier": "^3.5.3"
|
"prettier": "^3.5.3"
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-15
@@ -33,24 +33,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './cli/.nvmrc'
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: pnpm install && pnpm run build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
|
|
||||||
- run: pnpm install --frozen-lockfile
|
- name: Install dependencies
|
||||||
- run: pnpm build
|
run: mise run cli:install
|
||||||
- run: pnpm publish
|
|
||||||
|
- name: Run build
|
||||||
|
run: mise run cli:build
|
||||||
|
|
||||||
|
- name: Publish package
|
||||||
|
run: pnpm publish
|
||||||
if: ${{ github.event_name == 'release' }}
|
if: ${{ github.event_name == 'release' }}
|
||||||
env:
|
env:
|
||||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
|||||||
@@ -55,24 +55,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './docs/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
|
|
||||||
- name: Run install
|
- name: Run install
|
||||||
run: pnpm install
|
run: mise run docs:install
|
||||||
|
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: pnpm format
|
run: mise run docs:format-fix
|
||||||
|
|
||||||
- name: Run build
|
- name: Run build
|
||||||
run: pnpm build
|
run: mise run docs:build
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
|||||||
@@ -28,15 +28,11 @@ jobs:
|
|||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup mise
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
|
|
||||||
- name: Fix formatting
|
- name: Fix formatting
|
||||||
run: make install-all && make format-all
|
run: mise run server:format-fix && mise run web:format-fix && mise run docs:format-fix
|
||||||
|
|
||||||
- name: Commit and push
|
- name: Commit and push
|
||||||
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4
|
||||||
|
|||||||
@@ -46,15 +46,8 @@ jobs:
|
|||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
|
|
||||||
- name: Bump version
|
- name: Bump version
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -20,20 +20,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
|
|
||||||
# Setup .npmrc file to publish to npm
|
|
||||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './open-api/typescript-sdk/.nvmrc'
|
|
||||||
registry-url: 'https://registry.npmjs.org'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run sdk:install
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: pnpm build
|
run: mise run sdk:build
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: pnpm publish
|
run: pnpm publish
|
||||||
env:
|
env:
|
||||||
|
|||||||
+86
-184
@@ -72,27 +72,21 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run package manager install
|
- name: Run package manager install
|
||||||
run: pnpm install
|
run: mise run server:install
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: mise run server:lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: mise run server:format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: mise run server:check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run small tests & coverage
|
- name: Run small tests & coverage
|
||||||
run: pnpm test
|
run: mise run server:test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
name: Unit Test CLI
|
name: Unit Test CLI
|
||||||
@@ -109,30 +103,23 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './cli/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: pnpm install && pnpm run build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install
|
run: mise run cli:install
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: mise run cli:lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: mise run cli:format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: mise run cli:check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: mise run cli:test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
cli-unit-tests-win:
|
cli-unit-tests-win:
|
||||||
name: Unit Test CLI (Windows)
|
name: Unit Test CLI (Windows)
|
||||||
@@ -149,25 +136,18 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './cli/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Setup typescript-sdk
|
- name: Setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run cli:install
|
||||||
# Skip linter & formatter in Windows test.
|
# Skip linter & formatter in Windows test.
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: mise run cli:check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: mise run cli:test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
web-lint:
|
web-lint:
|
||||||
name: Lint Web
|
name: Lint Web
|
||||||
@@ -176,35 +156,25 @@ jobs:
|
|||||||
runs-on: mich
|
runs-on: mich
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./web
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './web/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
- name: Run install
|
||||||
- name: Run pnpm install
|
run: mise run web:install
|
||||||
run: pnpm rebuild && pnpm install --frozen-lockfile
|
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint:p
|
run: mise run web:lint-p
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: mise run web:format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run svelte checks
|
- name: Run svelte checks
|
||||||
run: pnpm check:svelte
|
run: mise run web:check-svelte
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
web-unit-tests:
|
web-unit-tests:
|
||||||
name: Test Web
|
name: Test Web
|
||||||
@@ -213,32 +183,22 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./web
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './web/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
- name: Run npm install
|
- name: Run npm install
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run web:install
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check:typescript
|
run: mise run web:check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run unit tests & coverage
|
- name: Run unit tests & coverage
|
||||||
run: pnpm test
|
run: mise run web:test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
i18n-tests:
|
i18n-tests:
|
||||||
name: Test i18n
|
name: Test i18n
|
||||||
@@ -252,18 +212,12 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './web/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm --filter=immich-web install --frozen-lockfile
|
run: mise run web:install
|
||||||
- name: Format
|
- name: Format
|
||||||
run: pnpm --filter=immich-web format:i18n
|
run: mise run i18n:format-fix
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
@@ -293,29 +247,22 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './e2e/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run e2e:install
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: pnpm lint
|
run: mise run e2e:lint
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: mise run e2e:format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run tsc
|
- name: Run tsc
|
||||||
run: pnpm check
|
run: mise run e2e:check
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
server-medium-tests:
|
server-medium-tests:
|
||||||
name: Medium Tests (Server)
|
name: Medium Tests (Server)
|
||||||
@@ -324,26 +271,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./server
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
- name: Run install
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true mise run server:install
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run pnpm install
|
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
|
||||||
- name: Run medium tests
|
- name: Run medium tests
|
||||||
run: pnpm test:medium
|
run: mise run server:test-medium
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
e2e-tests-server-cli:
|
e2e-tests-server-cli:
|
||||||
name: End-to-End Tests (Server & CLI)
|
name: End-to-End Tests (Server & CLI)
|
||||||
@@ -352,9 +290,6 @@ jobs:
|
|||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./e2e
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
@@ -364,34 +299,25 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './e2e/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run setup web
|
- name: Run setup web
|
||||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
run: mise run web:install && mise run web:svelte-kit-sync
|
||||||
working-directory: ./web
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run setup cli
|
- name: Run setup cli
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run cli:install && mise run cli:build
|
||||||
working-directory: ./cli
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run e2e:install
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
run: docker compose build
|
run: docker compose -f e2e/docker-compose.yml build
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run e2e tests (api & cli)
|
- name: Run e2e tests (api & cli)
|
||||||
run: pnpm test
|
run: mise run e2e:test
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
e2e-tests-web:
|
e2e-tests-web:
|
||||||
name: End-to-End Tests (Web)
|
name: End-to-End Tests (Web)
|
||||||
@@ -400,9 +326,6 @@ jobs:
|
|||||||
runs-on: ${{ matrix.runner }}
|
runs-on: ${{ matrix.runner }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./e2e
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
runner: [ubuntu-latest, ubuntu-24.04-arm]
|
||||||
@@ -412,29 +335,26 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './e2e/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run setup typescript-sdk
|
- name: Run setup typescript-sdk
|
||||||
run: pnpm install --frozen-lockfile && pnpm build
|
run: mise run sdk:install && mise run sdk:build
|
||||||
working-directory: ./open-api/typescript-sdk
|
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Install dependencies
|
- name: Run setup web
|
||||||
run: pnpm install --frozen-lockfile
|
run: mise run web:install && mise run web:svelte-kit-sync
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
- name: Run setup cli
|
||||||
|
run: mise run cli:install && mise run cli:build
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
run: npx playwright install chromium --only-shell
|
run: npx playwright install chromium --only-shell
|
||||||
|
working-directory: e2e
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
run: docker compose build
|
run: docker compose -f e2e/docker-compose.yml build
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
- name: Run e2e tests (web)
|
- name: Run e2e tests (web)
|
||||||
run: npx playwright test
|
run: mise run e2e:test-web
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
success-check-e2e:
|
success-check-e2e:
|
||||||
name: End-to-End Tests Success
|
name: End-to-End Tests Success
|
||||||
@@ -519,18 +439,12 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
- name: Run install
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
run: mise run github:install
|
||||||
with:
|
|
||||||
node-version-file: './.github/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Run pnpm install
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
- name: Run formatter
|
- name: Run formatter
|
||||||
run: pnpm format
|
run: mise run github:format
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
shellcheck:
|
shellcheck:
|
||||||
name: ShellCheck
|
name: ShellCheck
|
||||||
@@ -556,18 +470,12 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true mise run server:install
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm --filter immich build
|
run: mise run server:build
|
||||||
- name: Run API generation
|
- name: Run API generation
|
||||||
run: ./bin/generate-open-api.sh
|
run: ./bin/generate-open-api.sh
|
||||||
working-directory: open-api
|
working-directory: open-api
|
||||||
@@ -611,25 +519,19 @@ jobs:
|
|||||||
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- name: Setup pnpm
|
- name: Setup mise
|
||||||
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0
|
uses: jdx/mise-action@5ac50f778e26fac95da98d50503682459e86d566 # v3.2.0
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
||||||
with:
|
|
||||||
node-version-file: './server/.nvmrc'
|
|
||||||
cache: 'pnpm'
|
|
||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm build
|
run: mise run server:build
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
run: pnpm migrations:run
|
run: mise run server:migrations run
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: pnpm schema:reset
|
run: mise run server:schema-reset
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: pnpm migrations:generate src/TestMigration
|
run: mise run server:migrations generate src/TestMigration
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
id: verify-changed-files
|
id: verify-changed-files
|
||||||
@@ -646,7 +548,7 @@ jobs:
|
|||||||
cat ./src/*-TestMigration.ts
|
cat ./src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
- name: Run SQL generation
|
- name: Run SQL generation
|
||||||
run: pnpm sync:sql
|
run: mise run server:sql
|
||||||
env:
|
env:
|
||||||
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
DB_URL: postgres://postgres:postgres@localhost:5432/immich
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
+2
-16
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/cli",
|
"name": "@immich/cli",
|
||||||
"version": "2.2.87",
|
"version": "2.2.86",
|
||||||
"description": "Command Line Interface (CLI) for Immich",
|
"description": "Command Line Interface (CLI) for Immich",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./dist/index.js",
|
"exports": "./dist/index.js",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/micromatch": "^4.0.9",
|
"@types/micromatch": "^4.0.9",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/node": "^22.18.0",
|
"@types/node": "^22.17.1",
|
||||||
"@vitest/coverage-v8": "^3.0.0",
|
"@vitest/coverage-v8": "^3.0.0",
|
||||||
"byte-size": "^9.0.0",
|
"byte-size": "^9.0.0",
|
||||||
"cli-progress": "^3.12.0",
|
"cli-progress": "^3.12.0",
|
||||||
@@ -42,17 +42,6 @@
|
|||||||
"vitest-fetch-mock": "^0.4.0",
|
"vitest-fetch-mock": "^0.4.0",
|
||||||
"yaml": "^2.3.1"
|
"yaml": "^2.3.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"build": "vite build",
|
|
||||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
|
||||||
"lint:fix": "npm run lint -- --fix",
|
|
||||||
"prepack": "npm run build",
|
|
||||||
"test": "vitest",
|
|
||||||
"test:cov": "vitest --coverage",
|
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write .",
|
|
||||||
"check": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/immich-app/immich.git",
|
"url": "git+https://github.com/immich-app/immich.git",
|
||||||
@@ -67,8 +56,5 @@
|
|||||||
"fastq": "^1.17.1",
|
"fastq": "^1.17.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
@@ -5,7 +5,7 @@ After making any changes in the `server/src/schema`, a database migration need t
|
|||||||
1. Run the command
|
1. Run the command
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pnpm run migrations:generate <migration-name>
|
mise run server:migrations generate <migration-name>
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Check if the migration file makes sense.
|
2. Check if the migration file makes sense.
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ When contributing code through a pull request, please check the following:
|
|||||||
|
|
||||||
## Web Checks
|
## Web Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise run web:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise run web:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check:svelte` (Type checking via SvelteKit)
|
- [ ] `mise run web:check` (check typescript)
|
||||||
- [ ] `pnpm run check:typescript` (check typescript)
|
- [ ] `mise run web:check-svelte` (Type checking via SvelteKit)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise run web:test` (unit tests)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@@ -25,17 +25,18 @@ Run all web checks with `pnpm run check:all`
|
|||||||
|
|
||||||
## Server Checks
|
## Server Checks
|
||||||
|
|
||||||
- [ ] `pnpm run lint` (linting via ESLint)
|
- [ ] `mise run server:lint` (linting via ESLint)
|
||||||
- [ ] `pnpm run format` (formatting via Prettier)
|
- [ ] `mise run server:format` (formatting via Prettier)
|
||||||
- [ ] `pnpm run check` (Type checking via `tsc`)
|
- [ ] `mise run server:check` (type checking via `tsc`)
|
||||||
- [ ] `pnpm test` (unit tests)
|
- [ ] `mise run server:test` (unit tests)
|
||||||
|
- [ ] `mise run server:test-medium` (medium tests)
|
||||||
|
|
||||||
:::tip AIO
|
:::tip AIO
|
||||||
Run all server checks with `pnpm run check:all`
|
Run all server checks with `pnpm run check:all`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::info Auto Fix
|
:::info Auto Fix
|
||||||
You can use `pnpm run __:fix` to potentially correct some issues automatically for `pnpm run format` and `lint`.
|
You can use `mise run server:lint-fix` and `mise run server:format-fix` to potentially correct some issues automatically.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Mobile Checks
|
## Mobile Checks
|
||||||
|
|||||||
@@ -2,20 +2,6 @@
|
|||||||
"name": "documentation",
|
"name": "documentation",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
|
||||||
"docusaurus": "docusaurus",
|
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write .",
|
|
||||||
"start": "docusaurus start --port 3005",
|
|
||||||
"copy:openapi": "jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
|
|
||||||
"build": "npm run copy:openapi && docusaurus build",
|
|
||||||
"swizzle": "docusaurus swizzle",
|
|
||||||
"deploy": "docusaurus deploy",
|
|
||||||
"clear": "docusaurus clear",
|
|
||||||
"serve": "docusaurus serve",
|
|
||||||
"write-translations": "docusaurus write-translations",
|
|
||||||
"write-heading-ids": "docusaurus write-heading-ids"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@docusaurus/core": "~3.8.0",
|
"@docusaurus/core": "~3.8.0",
|
||||||
"@docusaurus/preset-classic": "~3.8.0",
|
"@docusaurus/preset-classic": "~3.8.0",
|
||||||
@@ -58,8 +44,5 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
-4
@@ -1,8 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"label": "v1.141.0",
|
|
||||||
"url": "https://v1.141.0.archive.immich.app"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": "v1.140.1",
|
"label": "v1.140.1",
|
||||||
"url": "https://v1.140.1.archive.immich.app"
|
"url": "https://v1.140.1.archive.immich.app"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
+3
-17
@@ -1,20 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-e2e",
|
"name": "immich-e2e",
|
||||||
"version": "1.141.0",
|
"version": "1.140.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
|
||||||
"test": "vitest --run",
|
|
||||||
"test:watch": "vitest",
|
|
||||||
"test:web": "npx playwright test",
|
|
||||||
"start:web": "npx playwright test --ui",
|
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write .",
|
|
||||||
"lint": "eslint \"src/**/*.ts\" --max-warnings 0",
|
|
||||||
"lint:fix": "npm run lint -- --fix",
|
|
||||||
"check": "tsc --noEmit"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
@@ -26,7 +15,7 @@
|
|||||||
"@playwright/test": "^1.44.1",
|
"@playwright/test": "^1.44.1",
|
||||||
"@socket.io/component-emitter": "^3.1.2",
|
"@socket.io/component-emitter": "^3.1.2",
|
||||||
"@types/luxon": "^3.4.2",
|
"@types/luxon": "^3.4.2",
|
||||||
"@types/node": "^22.18.0",
|
"@types/node": "^22.17.1",
|
||||||
"@types/oidc-provider": "^9.0.0",
|
"@types/oidc-provider": "^9.0.0",
|
||||||
"@types/pg": "^8.15.1",
|
"@types/pg": "^8.15.1",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
@@ -45,15 +34,12 @@
|
|||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"prettier-plugin-organize-imports": "^4.0.0",
|
"prettier-plugin-organize-imports": "^4.0.0",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.0",
|
||||||
"socket.io-client": "^4.7.4",
|
"socket.io-client": "^4.7.4",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"utimes": "^5.2.1",
|
"utimes": "^5.2.1",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1417,8 +1417,6 @@
|
|||||||
"open_the_search_filters": "Open the search filters",
|
"open_the_search_filters": "Open the search filters",
|
||||||
"options": "Options",
|
"options": "Options",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
"organize_into_albums": "Organize into albums",
|
|
||||||
"organize_into_albums_description": "Put existing photos into albums using current sync settings",
|
|
||||||
"organize_your_library": "Organize your library",
|
"organize_your_library": "Organize your library",
|
||||||
"original": "original",
|
"original": "original",
|
||||||
"other": "Other",
|
"other": "Other",
|
||||||
|
|||||||
@@ -13,3 +13,300 @@ postinstall = "chmod +x $MISE_TOOL_INSTALL_PATH/dcm"
|
|||||||
experimental = true
|
experimental = true
|
||||||
lockfile = true
|
lockfile = true
|
||||||
pin = true
|
pin = true
|
||||||
|
|
||||||
|
# .github
|
||||||
|
[tasks."github:install"]
|
||||||
|
run = "pnpm install --filter github --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."github:format"]
|
||||||
|
env._.path = "./.github/node_modules/.bin"
|
||||||
|
dir = ".github"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."github:format-fix"]
|
||||||
|
env._.path = "./.github/node_modules/.bin"
|
||||||
|
dir = ".github"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
# @immich/cli
|
||||||
|
[tasks."cli:install"]
|
||||||
|
run = "pnpm install --filter @immich/cli --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."cli:build"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "vite build"
|
||||||
|
|
||||||
|
[tasks."cli:test"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "vite"
|
||||||
|
|
||||||
|
[tasks."cli:lint"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "eslint \"src/**/*.ts\" --max-warnings 0"
|
||||||
|
|
||||||
|
[tasks."cli:lint-fix"]
|
||||||
|
run = "mise run cli:lint --fix"
|
||||||
|
|
||||||
|
[tasks."cli:format"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."cli:format-fix"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
[tasks."cli:check"]
|
||||||
|
env._.path = "./cli/node_modules/.bin"
|
||||||
|
dir = "cli"
|
||||||
|
run = "tsc --noEmit"
|
||||||
|
|
||||||
|
# @immich/sdk
|
||||||
|
[tasks."sdk:install"]
|
||||||
|
run = "pnpm install --filter @immich/sdk --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."sdk:build"]
|
||||||
|
env._.path = "./open-api/typescript-sdk/node_modules/.bin"
|
||||||
|
dir = "./open-api/typescript-sdk"
|
||||||
|
run = "tsc"
|
||||||
|
|
||||||
|
# docs
|
||||||
|
[tasks."docs:install"]
|
||||||
|
run = "pnpm install --filter documentation --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."docs:start"]
|
||||||
|
env._.path = "./docs/node_modules/.bin"
|
||||||
|
dir = "docs"
|
||||||
|
run = "docusaurus --port 3005"
|
||||||
|
|
||||||
|
[tasks."docs:build"]
|
||||||
|
env._.path = "./docs/node_modules/.bin"
|
||||||
|
dir = "docs"
|
||||||
|
run = [
|
||||||
|
"jq -c < ../open-api/immich-openapi-specs.json > ./static/openapi.json || exit 0",
|
||||||
|
"docusaurus build",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
[tasks."docs:preview"]
|
||||||
|
env._.path = "./docs/node_modules/.bin"
|
||||||
|
dir = "docs"
|
||||||
|
run = "docusaurus serve"
|
||||||
|
|
||||||
|
|
||||||
|
[tasks."docs:format"]
|
||||||
|
env._.path = "./docs/node_modules/.bin"
|
||||||
|
dir = "docs"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."docs:format-fix"]
|
||||||
|
env._.path = "./docs/node_modules/.bin"
|
||||||
|
dir = "docs"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
|
||||||
|
# e2e
|
||||||
|
[tasks."e2e:install"]
|
||||||
|
run = "pnpm install --filter immich-e2e --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."e2e:test"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "vitest --run"
|
||||||
|
|
||||||
|
[tasks."e2e:test-web"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "playwright test"
|
||||||
|
|
||||||
|
[tasks."e2e:format"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."e2e:format-fix"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
[tasks."e2e:lint"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "eslint \"src/**/*.ts\" --max-warnings 0"
|
||||||
|
|
||||||
|
[tasks."e2e:lint-fix"]
|
||||||
|
run = "mise run e2e:lint --fix"
|
||||||
|
|
||||||
|
[tasks."e2e:check"]
|
||||||
|
env._.path = "./e2e/node_modules/.bin"
|
||||||
|
dir = "e2e"
|
||||||
|
run = "tsc --noEmit"
|
||||||
|
|
||||||
|
# i18n
|
||||||
|
[tasks."i18n:format"]
|
||||||
|
run = "mise run i18n:format-fix"
|
||||||
|
|
||||||
|
[tasks."i18n:format-fix"]
|
||||||
|
run = "pnpm dlx sort-json ./i18n/*.json"
|
||||||
|
|
||||||
|
|
||||||
|
# server
|
||||||
|
[tasks."server:install"]
|
||||||
|
run = "pnpm install --filter immich --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."server:build"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "nest build"
|
||||||
|
|
||||||
|
[tasks."server:test"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "vitest --config test/vitest.config.mjs"
|
||||||
|
|
||||||
|
[tasks."server:test-medium"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "vitest --config test/vitest.config.medium.mjs"
|
||||||
|
|
||||||
|
[tasks."server:format"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."server:format-fix"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
[tasks."server:lint"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "eslint \"src/**/*.ts\" \"test/**/*.ts\" --max-warnings 0"
|
||||||
|
|
||||||
|
[tasks."server:lint-fix"]
|
||||||
|
run = "mise run server:lint --fix"
|
||||||
|
|
||||||
|
[tasks."server:check"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "tsc --noEmit"
|
||||||
|
|
||||||
|
[tasks."server:sql"]
|
||||||
|
dir = "server"
|
||||||
|
run = "node ./dist/bin/sync-open-api.js"
|
||||||
|
|
||||||
|
[tasks."server:open-api"]
|
||||||
|
dir = "server"
|
||||||
|
run = "node ./dist/bin/sync-open-api.js"
|
||||||
|
|
||||||
|
[tasks."server:migrations"]
|
||||||
|
dir = "server"
|
||||||
|
run = "node ./dist/bin/migrations.js"
|
||||||
|
description = "Run database migration commands (create, generate, run, debug, or query)"
|
||||||
|
|
||||||
|
[tasks."server:schema-drop"]
|
||||||
|
run = "mise run server:migrations query 'DROP schema public cascade; CREATE schema public;'"
|
||||||
|
|
||||||
|
[tasks."server:schema-reset"]
|
||||||
|
run = "mise run server:schema-drop && mise run server:migrations run"
|
||||||
|
|
||||||
|
[tasks."server:email-dev"]
|
||||||
|
env._.path = "./server/node_modules/.bin"
|
||||||
|
dir = "server"
|
||||||
|
run = "email dev -p 3050 --dir src/emails"
|
||||||
|
|
||||||
|
[tasks."server:checklist"]
|
||||||
|
run = [
|
||||||
|
"mise run server:install",
|
||||||
|
"mise run server:format",
|
||||||
|
"mise run server:lint",
|
||||||
|
"mise run server:check",
|
||||||
|
"mise run server:test-medium --run",
|
||||||
|
"mise run server:test --run",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# web
|
||||||
|
[tasks."web:install"]
|
||||||
|
run = "pnpm install --filter immich-web --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks."web:svelte-kit-sync"]
|
||||||
|
env._.path = "./web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "svelte-kit sync"
|
||||||
|
|
||||||
|
[tasks."web:build"]
|
||||||
|
env._.path = "./web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "vite build"
|
||||||
|
|
||||||
|
[tasks."web:build-stats"]
|
||||||
|
env.BUILD_STATS = "true"
|
||||||
|
env._.path = "./web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "vite build"
|
||||||
|
|
||||||
|
[tasks."web:preview"]
|
||||||
|
env._.path = "./web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "vite preview"
|
||||||
|
|
||||||
|
[tasks."web:start"]
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "vite dev --host 0.0.0.0 --port 3000"
|
||||||
|
|
||||||
|
[tasks."web:test"]
|
||||||
|
depends = "web:svelte-kit-sync"
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "vitest"
|
||||||
|
|
||||||
|
[tasks."web:format"]
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "prettier --check ."
|
||||||
|
|
||||||
|
[tasks."web:format-fix"]
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "prettier --write ."
|
||||||
|
|
||||||
|
[tasks."web:lint"]
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "eslint . --max-warnings 0"
|
||||||
|
|
||||||
|
[tasks."web:lint-p"]
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "eslint-p . --max-warnings 0 --concurrency=4"
|
||||||
|
|
||||||
|
[tasks."web:lint-fix"]
|
||||||
|
run = "mise run web:lint --fix"
|
||||||
|
|
||||||
|
[tasks."web:check"]
|
||||||
|
depends = "web:svelte-kit-sync"
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "tsc --noEmit"
|
||||||
|
|
||||||
|
[tasks."web:check-svelte"]
|
||||||
|
depends = "web:svelte-kit-sync"
|
||||||
|
env._.path = "web/node_modules/.bin"
|
||||||
|
dir = "web"
|
||||||
|
run = "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte"
|
||||||
|
|
||||||
|
[tasks."web:checklist"]
|
||||||
|
run = [
|
||||||
|
"mise run web:install",
|
||||||
|
"mise run web:format",
|
||||||
|
"mise run web:check",
|
||||||
|
"mise run web:test --run",
|
||||||
|
"mise run web:lint",
|
||||||
|
]
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ platform :android do
|
|||||||
task: 'bundle',
|
task: 'bundle',
|
||||||
build_type: 'Release',
|
build_type: 'Release',
|
||||||
properties: {
|
properties: {
|
||||||
"android.injected.version.code" => 3012,
|
"android.injected.version.code" => 3011,
|
||||||
"android.injected.version.name" => "1.141.0",
|
"android.injected.version.name" => "1.140.1",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||||
|
|||||||
-1
File diff suppressed because one or more lines are too long
@@ -22,7 +22,7 @@ platform :ios do
|
|||||||
path: "./Runner.xcodeproj",
|
path: "./Runner.xcodeproj",
|
||||||
)
|
)
|
||||||
increment_version_number(
|
increment_version_number(
|
||||||
version_number: "1.141.0"
|
version_number: "1.140.1"
|
||||||
)
|
)
|
||||||
increment_build_number(
|
increment_build_number(
|
||||||
build_number: latest_testflight_build_number + 1,
|
build_number: latest_testflight_build_number + 1,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ class LocalAlbum {
|
|||||||
|
|
||||||
final int assetCount;
|
final int assetCount;
|
||||||
final BackupSelection backupSelection;
|
final BackupSelection backupSelection;
|
||||||
final String? linkedRemoteAlbumId;
|
|
||||||
|
|
||||||
const LocalAlbum({
|
const LocalAlbum({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -24,7 +23,6 @@ class LocalAlbum {
|
|||||||
this.assetCount = 0,
|
this.assetCount = 0,
|
||||||
this.backupSelection = BackupSelection.none,
|
this.backupSelection = BackupSelection.none,
|
||||||
this.isIosSharedAlbum = false,
|
this.isIosSharedAlbum = false,
|
||||||
this.linkedRemoteAlbumId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
LocalAlbum copyWith({
|
LocalAlbum copyWith({
|
||||||
@@ -34,7 +32,6 @@ class LocalAlbum {
|
|||||||
int? assetCount,
|
int? assetCount,
|
||||||
BackupSelection? backupSelection,
|
BackupSelection? backupSelection,
|
||||||
bool? isIosSharedAlbum,
|
bool? isIosSharedAlbum,
|
||||||
String? linkedRemoteAlbumId,
|
|
||||||
}) {
|
}) {
|
||||||
return LocalAlbum(
|
return LocalAlbum(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -43,7 +40,6 @@ class LocalAlbum {
|
|||||||
assetCount: assetCount ?? this.assetCount,
|
assetCount: assetCount ?? this.assetCount,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +53,7 @@ class LocalAlbum {
|
|||||||
other.updatedAt == updatedAt &&
|
other.updatedAt == updatedAt &&
|
||||||
other.assetCount == assetCount &&
|
other.assetCount == assetCount &&
|
||||||
other.backupSelection == backupSelection &&
|
other.backupSelection == backupSelection &&
|
||||||
other.isIosSharedAlbum == isIosSharedAlbum &&
|
other.isIosSharedAlbum == isIosSharedAlbum;
|
||||||
other.linkedRemoteAlbumId == linkedRemoteAlbumId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -68,8 +63,7 @@ class LocalAlbum {
|
|||||||
updatedAt.hashCode ^
|
updatedAt.hashCode ^
|
||||||
assetCount.hashCode ^
|
assetCount.hashCode ^
|
||||||
backupSelection.hashCode ^
|
backupSelection.hashCode ^
|
||||||
isIosSharedAlbum.hashCode ^
|
isIosSharedAlbum.hashCode;
|
||||||
linkedRemoteAlbumId.hashCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -81,7 +75,6 @@ updatedAt: $updatedAt,
|
|||||||
assetCount: $assetCount,
|
assetCount: $assetCount,
|
||||||
backupSelection: $backupSelection,
|
backupSelection: $backupSelection,
|
||||||
isIosSharedAlbum: $isIosSharedAlbum
|
isIosSharedAlbum: $isIosSharedAlbum
|
||||||
linkedRemoteAlbumId: $linkedRemoteAlbumId,
|
|
||||||
}''';
|
}''';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,16 +22,4 @@ class LocalAlbumService {
|
|||||||
Future<int> getCount() {
|
Future<int> getCount() {
|
||||||
return _repository.getCount();
|
return _repository.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> unlinkRemoteAlbum(String id) async {
|
|
||||||
return _repository.unlinkRemoteAlbum(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async {
|
|
||||||
return _repository.linkRemoteAlbum(localAlbumId, remoteAlbumId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<LocalAlbum>> getBackupAlbums() {
|
|
||||||
return _repository.getBackupAlbums();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,6 @@ class RemoteAlbumService {
|
|||||||
return _repository.get(albumId);
|
return _repository.get(albumId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
|
|
||||||
return _repository.getByName(albumName, ownerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<List<RemoteAlbum>> sortAlbums(
|
Future<List<RemoteAlbum>> sortAlbums(
|
||||||
List<RemoteAlbum> albums,
|
List<RemoteAlbum> albums,
|
||||||
RemoteAlbumSortMode sortMode, {
|
RemoteAlbumSortMode sortMode, {
|
||||||
@@ -84,6 +80,7 @@ class RemoteAlbumService {
|
|||||||
|
|
||||||
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
|
Future<RemoteAlbum> createAlbum({required String title, required List<String> assetIds, String? description}) async {
|
||||||
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
|
final album = await _albumApiRepository.createDriftAlbum(title, description: description, assetIds: assetIds);
|
||||||
|
|
||||||
await _repository.create(album, assetIds);
|
await _repository.create(album, assetIds);
|
||||||
|
|
||||||
return album;
|
return album;
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/repositories/local_album.repository.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
|
|
||||||
import 'package:immich_mobile/repositories/drift_album_api_repository.dart';
|
|
||||||
|
|
||||||
final syncLinkedAlbumServiceProvider = Provider(
|
|
||||||
(ref) => SyncLinkedAlbumService(
|
|
||||||
ref.watch(localAlbumRepository),
|
|
||||||
ref.watch(remoteAlbumRepository),
|
|
||||||
ref.watch(driftAlbumApiRepositoryProvider),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
class SyncLinkedAlbumService {
|
|
||||||
final DriftLocalAlbumRepository _localAlbumRepository;
|
|
||||||
final DriftRemoteAlbumRepository _remoteAlbumRepository;
|
|
||||||
final DriftAlbumApiRepository _albumApiRepository;
|
|
||||||
|
|
||||||
const SyncLinkedAlbumService(this._localAlbumRepository, this._remoteAlbumRepository, this._albumApiRepository);
|
|
||||||
|
|
||||||
Future<void> syncLinkedAlbums(String userId) async {
|
|
||||||
final selectedAlbums = await _localAlbumRepository.getBackupAlbums();
|
|
||||||
|
|
||||||
await Future.wait(
|
|
||||||
selectedAlbums.map((localAlbum) async {
|
|
||||||
final linkedRemoteAlbumId = localAlbum.linkedRemoteAlbumId;
|
|
||||||
if (linkedRemoteAlbumId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final remoteAlbum = await _remoteAlbumRepository.get(linkedRemoteAlbumId);
|
|
||||||
if (remoteAlbum == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get assets that are uploaded but not in the remote album
|
|
||||||
final assetIds = await _remoteAlbumRepository.getLinkedAssetIds(userId, localAlbum.id, linkedRemoteAlbumId);
|
|
||||||
|
|
||||||
if (assetIds.isNotEmpty) {
|
|
||||||
final album = await _albumApiRepository.addAssets(remoteAlbum.id, assetIds);
|
|
||||||
await _remoteAlbumRepository.addAssets(remoteAlbum.id, album.added);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> manageLinkedAlbums(List<LocalAlbum> localAlbums, String ownerId) async {
|
|
||||||
for (final album in localAlbums) {
|
|
||||||
await _processLocalAlbum(album, ownerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes a single local album to ensure proper linking with remote albums
|
|
||||||
Future<void> _processLocalAlbum(LocalAlbum localAlbum, String ownerId) {
|
|
||||||
final hasLinkedRemoteAlbum = localAlbum.linkedRemoteAlbumId != null;
|
|
||||||
|
|
||||||
if (hasLinkedRemoteAlbum) {
|
|
||||||
return _handleLinkedAlbum(localAlbum);
|
|
||||||
} else {
|
|
||||||
return _handleUnlinkedAlbum(localAlbum, ownerId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles albums that are already linked to a remote album
|
|
||||||
Future<void> _handleLinkedAlbum(LocalAlbum localAlbum) async {
|
|
||||||
final remoteAlbumId = localAlbum.linkedRemoteAlbumId!;
|
|
||||||
final remoteAlbum = await _remoteAlbumRepository.get(remoteAlbumId);
|
|
||||||
|
|
||||||
final remoteAlbumExists = remoteAlbum != null;
|
|
||||||
if (!remoteAlbumExists) {
|
|
||||||
return _localAlbumRepository.unlinkRemoteAlbum(localAlbum.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles albums that are not linked to any remote album
|
|
||||||
Future<void> _handleUnlinkedAlbum(LocalAlbum localAlbum, String ownerId) async {
|
|
||||||
final existingRemoteAlbum = await _remoteAlbumRepository.getByName(localAlbum.name, ownerId);
|
|
||||||
|
|
||||||
if (existingRemoteAlbum != null) {
|
|
||||||
return _linkToExistingRemoteAlbum(localAlbum, existingRemoteAlbum);
|
|
||||||
} else {
|
|
||||||
return _createAndLinkNewRemoteAlbum(localAlbum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links a local album to an existing remote album
|
|
||||||
Future<void> _linkToExistingRemoteAlbum(LocalAlbum localAlbum, dynamic existingRemoteAlbum) {
|
|
||||||
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, existingRemoteAlbum.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new remote album and links it to the local album
|
|
||||||
Future<void> _createAndLinkNewRemoteAlbum(LocalAlbum localAlbum) async {
|
|
||||||
debugPrint("Creating new remote album for local album: ${localAlbum.name}");
|
|
||||||
final newRemoteAlbum = await _albumApiRepository.createDriftAlbum(localAlbum.name, assetIds: []);
|
|
||||||
await _remoteAlbumRepository.create(newRemoteAlbum, []);
|
|
||||||
return _localAlbumRepository.linkRemoteAlbum(localAlbum.id, newRemoteAlbum.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
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';
|
||||||
import 'package:worker_manager/worker_manager.dart';
|
import 'package:worker_manager/worker_manager.dart';
|
||||||
@@ -156,11 +155,6 @@ class BackgroundSyncManager {
|
|||||||
_syncWebsocketTask = null;
|
_syncWebsocketTask = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> syncLinkedAlbum() {
|
|
||||||
final task = runInIsolateGentle(computation: syncLinkedAlbumsIsolated);
|
|
||||||
return task.future;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
Cancelable<void> _handleWsAssetUploadReadyV1Batch(List<dynamic> batchData) => runInIsolateGentle(
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
|
|
||||||
Future<void> syncLinkedAlbumsIsolated(ProviderContainer ref) {
|
|
||||||
final user = ref.read(currentUserProvider);
|
|
||||||
if (user == null) {
|
|
||||||
return Future.value();
|
|
||||||
}
|
|
||||||
return ref.read(syncLinkedAlbumServiceProvider).syncLinkedAlbums(user.id);
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
|
||||||
|
|
||||||
class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
||||||
@@ -13,26 +11,9 @@ class LocalAlbumEntity extends Table with DriftDefaultsMixin {
|
|||||||
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
IntColumn get backupSelection => intEnum<BackupSelection>()();
|
||||||
BoolColumn get isIosSharedAlbum => boolean().withDefault(const Constant(false))();
|
BoolColumn get isIosSharedAlbum => boolean().withDefault(const Constant(false))();
|
||||||
|
|
||||||
// // Linked album for putting assets to the remote album after finished uploading
|
|
||||||
TextColumn get linkedRemoteAlbumId =>
|
|
||||||
text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.setNull).nullable()();
|
|
||||||
|
|
||||||
// Used for mark & sweep
|
// Used for mark & sweep
|
||||||
BoolColumn get marker_ => boolean().nullable()();
|
BoolColumn get marker_ => boolean().nullable()();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Set<Column> get primaryKey => {id};
|
Set<Column> get primaryKey => {id};
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalAlbumEntityDataHelper on LocalAlbumEntityData {
|
|
||||||
LocalAlbum toDto({int assetCount = 0}) {
|
|
||||||
return LocalAlbum(
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
updatedAt: updatedAt,
|
|
||||||
assetCount: assetCount,
|
|
||||||
backupSelection: backupSelection,
|
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+20
-253
@@ -7,9 +7,6 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart' as i2;
|
|||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'
|
||||||
as i3;
|
as i3;
|
||||||
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i5;
|
|
||||||
import 'package:drift/internal/modular.dart' as i6;
|
|
||||||
|
|
||||||
typedef $$LocalAlbumEntityTableCreateCompanionBuilder =
|
typedef $$LocalAlbumEntityTableCreateCompanionBuilder =
|
||||||
i1.LocalAlbumEntityCompanion Function({
|
i1.LocalAlbumEntityCompanion Function({
|
||||||
@@ -18,7 +15,6 @@ typedef $$LocalAlbumEntityTableCreateCompanionBuilder =
|
|||||||
i0.Value<DateTime> updatedAt,
|
i0.Value<DateTime> updatedAt,
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
i0.Value<bool> isIosSharedAlbum,
|
i0.Value<bool> isIosSharedAlbum,
|
||||||
i0.Value<String?> linkedRemoteAlbumId,
|
|
||||||
i0.Value<bool?> marker_,
|
i0.Value<bool?> marker_,
|
||||||
});
|
});
|
||||||
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder =
|
typedef $$LocalAlbumEntityTableUpdateCompanionBuilder =
|
||||||
@@ -28,57 +24,9 @@ typedef $$LocalAlbumEntityTableUpdateCompanionBuilder =
|
|||||||
i0.Value<DateTime> updatedAt,
|
i0.Value<DateTime> updatedAt,
|
||||||
i0.Value<i2.BackupSelection> backupSelection,
|
i0.Value<i2.BackupSelection> backupSelection,
|
||||||
i0.Value<bool> isIosSharedAlbum,
|
i0.Value<bool> isIosSharedAlbum,
|
||||||
i0.Value<String?> linkedRemoteAlbumId,
|
|
||||||
i0.Value<bool?> marker_,
|
i0.Value<bool?> marker_,
|
||||||
});
|
});
|
||||||
|
|
||||||
final class $$LocalAlbumEntityTableReferences
|
|
||||||
extends
|
|
||||||
i0.BaseReferences<
|
|
||||||
i0.GeneratedDatabase,
|
|
||||||
i1.$LocalAlbumEntityTable,
|
|
||||||
i1.LocalAlbumEntityData
|
|
||||||
> {
|
|
||||||
$$LocalAlbumEntityTableReferences(
|
|
||||||
super.$_db,
|
|
||||||
super.$_table,
|
|
||||||
super.$_typedResult,
|
|
||||||
);
|
|
||||||
|
|
||||||
static i5.$RemoteAlbumEntityTable _linkedRemoteAlbumIdTable(
|
|
||||||
i0.GeneratedDatabase db,
|
|
||||||
) => i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
|
|
||||||
.createAlias(
|
|
||||||
i0.$_aliasNameGenerator(
|
|
||||||
i6.ReadDatabaseContainer(db)
|
|
||||||
.resultSet<i1.$LocalAlbumEntityTable>('local_album_entity')
|
|
||||||
.linkedRemoteAlbumId,
|
|
||||||
i6.ReadDatabaseContainer(
|
|
||||||
db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity').id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableProcessedTableManager? get linkedRemoteAlbumId {
|
|
||||||
final $_column = $_itemColumn<String>('linked_remote_album_id');
|
|
||||||
if ($_column == null) return null;
|
|
||||||
final manager = i5
|
|
||||||
.$$RemoteAlbumEntityTableTableManager(
|
|
||||||
$_db,
|
|
||||||
i6.ReadDatabaseContainer(
|
|
||||||
$_db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
)
|
|
||||||
.filter((f) => f.id.sqlEquals($_column));
|
|
||||||
final item = $_typedResult.readTableOrNull(_linkedRemoteAlbumIdTable($_db));
|
|
||||||
if (item == null) return manager;
|
|
||||||
return i0.ProcessedTableManager(
|
|
||||||
manager.$state.copyWith(prefetchedData: [item]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class $$LocalAlbumEntityTableFilterComposer
|
class $$LocalAlbumEntityTableFilterComposer
|
||||||
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
|
extends i0.Composer<i0.GeneratedDatabase, i1.$LocalAlbumEntityTable> {
|
||||||
$$LocalAlbumEntityTableFilterComposer({
|
$$LocalAlbumEntityTableFilterComposer({
|
||||||
@@ -118,33 +66,6 @@ class $$LocalAlbumEntityTableFilterComposer
|
|||||||
column: $table.marker_,
|
column: $table.marker_,
|
||||||
builder: (column) => i0.ColumnFilters(column),
|
builder: (column) => i0.ColumnFilters(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableFilterComposer get linkedRemoteAlbumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder:
|
|
||||||
(
|
|
||||||
joinBuilder, {
|
|
||||||
$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
}) => i5.$$RemoteAlbumEntityTableFilterComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAlbumEntityTableOrderingComposer
|
class $$LocalAlbumEntityTableOrderingComposer
|
||||||
@@ -185,34 +106,6 @@ class $$LocalAlbumEntityTableOrderingComposer
|
|||||||
column: $table.marker_,
|
column: $table.marker_,
|
||||||
builder: (column) => i0.ColumnOrderings(column),
|
builder: (column) => i0.ColumnOrderings(column),
|
||||||
);
|
);
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableOrderingComposer get linkedRemoteAlbumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder:
|
|
||||||
(
|
|
||||||
joinBuilder, {
|
|
||||||
$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
}) => i5.$$RemoteAlbumEntityTableOrderingComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAlbumEntityTableAnnotationComposer
|
class $$LocalAlbumEntityTableAnnotationComposer
|
||||||
@@ -246,34 +139,6 @@ class $$LocalAlbumEntityTableAnnotationComposer
|
|||||||
|
|
||||||
i0.GeneratedColumn<bool> get marker_ =>
|
i0.GeneratedColumn<bool> get marker_ =>
|
||||||
$composableBuilder(column: $table.marker_, builder: (column) => column);
|
$composableBuilder(column: $table.marker_, builder: (column) => column);
|
||||||
|
|
||||||
i5.$$RemoteAlbumEntityTableAnnotationComposer get linkedRemoteAlbumId {
|
|
||||||
final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
|
|
||||||
$composerBuilder(
|
|
||||||
composer: this,
|
|
||||||
getCurrentColumn: (t) => t.linkedRemoteAlbumId,
|
|
||||||
referencedTable: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
getReferencedColumn: (t) => t.id,
|
|
||||||
builder:
|
|
||||||
(
|
|
||||||
joinBuilder, {
|
|
||||||
$addJoinBuilderToRootComposer,
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
}) => i5.$$RemoteAlbumEntityTableAnnotationComposer(
|
|
||||||
$db: $db,
|
|
||||||
$table: i6.ReadDatabaseContainer(
|
|
||||||
$db,
|
|
||||||
).resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
|
|
||||||
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
|
|
||||||
joinBuilder: joinBuilder,
|
|
||||||
$removeJoinBuilderFromRootComposer:
|
|
||||||
$removeJoinBuilderFromRootComposer,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
return composer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class $$LocalAlbumEntityTableTableManager
|
class $$LocalAlbumEntityTableTableManager
|
||||||
@@ -287,9 +152,16 @@ class $$LocalAlbumEntityTableTableManager
|
|||||||
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
||||||
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
||||||
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
||||||
(i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences),
|
(
|
||||||
|
i1.LocalAlbumEntityData,
|
||||||
|
i0.BaseReferences<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$LocalAlbumEntityTable,
|
||||||
|
i1.LocalAlbumEntityData
|
||||||
|
>,
|
||||||
|
),
|
||||||
i1.LocalAlbumEntityData,
|
i1.LocalAlbumEntityData,
|
||||||
i0.PrefetchHooks Function({bool linkedRemoteAlbumId})
|
i0.PrefetchHooks Function()
|
||||||
> {
|
> {
|
||||||
$$LocalAlbumEntityTableTableManager(
|
$$LocalAlbumEntityTableTableManager(
|
||||||
i0.GeneratedDatabase db,
|
i0.GeneratedDatabase db,
|
||||||
@@ -315,7 +187,6 @@ class $$LocalAlbumEntityTableTableManager
|
|||||||
i0.Value<i2.BackupSelection> backupSelection =
|
i0.Value<i2.BackupSelection> backupSelection =
|
||||||
const i0.Value.absent(),
|
const i0.Value.absent(),
|
||||||
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||||
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
|
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||||
}) => i1.LocalAlbumEntityCompanion(
|
}) => i1.LocalAlbumEntityCompanion(
|
||||||
id: id,
|
id: id,
|
||||||
@@ -323,7 +194,6 @@ class $$LocalAlbumEntityTableTableManager
|
|||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
backupSelection: backupSelection,
|
backupSelection: backupSelection,
|
||||||
isIosSharedAlbum: isIosSharedAlbum,
|
isIosSharedAlbum: isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId,
|
|
||||||
marker_: marker_,
|
marker_: marker_,
|
||||||
),
|
),
|
||||||
createCompanionCallback:
|
createCompanionCallback:
|
||||||
@@ -333,7 +203,6 @@ class $$LocalAlbumEntityTableTableManager
|
|||||||
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
i0.Value<DateTime> updatedAt = const i0.Value.absent(),
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
i0.Value<bool> isIosSharedAlbum = const i0.Value.absent(),
|
||||||
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
|
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||||
}) => i1.LocalAlbumEntityCompanion.insert(
|
}) => i1.LocalAlbumEntityCompanion.insert(
|
||||||
id: id,
|
id: id,
|
||||||
@@ -341,60 +210,12 @@ class $$LocalAlbumEntityTableTableManager
|
|||||||
updatedAt: updatedAt,
|
updatedAt: updatedAt,
|
||||||
backupSelection: backupSelection,
|
backupSelection: backupSelection,
|
||||||
isIosSharedAlbum: isIosSharedAlbum,
|
isIosSharedAlbum: isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId,
|
|
||||||
marker_: marker_,
|
marker_: marker_,
|
||||||
),
|
),
|
||||||
withReferenceMapper: (p0) => p0
|
withReferenceMapper: (p0) => p0
|
||||||
.map(
|
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
|
||||||
(e) => (
|
|
||||||
e.readTable(table),
|
|
||||||
i1.$$LocalAlbumEntityTableReferences(db, table, e),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
.toList(),
|
||||||
prefetchHooksCallback: ({linkedRemoteAlbumId = false}) {
|
prefetchHooksCallback: null,
|
||||||
return i0.PrefetchHooks(
|
|
||||||
db: db,
|
|
||||||
explicitlyWatchedTables: [],
|
|
||||||
addJoins:
|
|
||||||
<
|
|
||||||
T extends i0.TableManagerState<
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic,
|
|
||||||
dynamic
|
|
||||||
>
|
|
||||||
>(state) {
|
|
||||||
if (linkedRemoteAlbumId) {
|
|
||||||
state =
|
|
||||||
state.withJoin(
|
|
||||||
currentTable: table,
|
|
||||||
currentColumn: table.linkedRemoteAlbumId,
|
|
||||||
referencedTable: i1
|
|
||||||
.$$LocalAlbumEntityTableReferences
|
|
||||||
._linkedRemoteAlbumIdTable(db),
|
|
||||||
referencedColumn: i1
|
|
||||||
.$$LocalAlbumEntityTableReferences
|
|
||||||
._linkedRemoteAlbumIdTable(db)
|
|
||||||
.id,
|
|
||||||
)
|
|
||||||
as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
},
|
|
||||||
getPrefetchedDataCallback: (items) async {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -409,9 +230,16 @@ typedef $$LocalAlbumEntityTableProcessedTableManager =
|
|||||||
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
i1.$$LocalAlbumEntityTableAnnotationComposer,
|
||||||
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
$$LocalAlbumEntityTableCreateCompanionBuilder,
|
||||||
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
$$LocalAlbumEntityTableUpdateCompanionBuilder,
|
||||||
(i1.LocalAlbumEntityData, i1.$$LocalAlbumEntityTableReferences),
|
(
|
||||||
|
i1.LocalAlbumEntityData,
|
||||||
|
i0.BaseReferences<
|
||||||
|
i0.GeneratedDatabase,
|
||||||
|
i1.$LocalAlbumEntityTable,
|
||||||
|
i1.LocalAlbumEntityData
|
||||||
|
>,
|
||||||
|
),
|
||||||
i1.LocalAlbumEntityData,
|
i1.LocalAlbumEntityData,
|
||||||
i0.PrefetchHooks Function({bool linkedRemoteAlbumId})
|
i0.PrefetchHooks Function()
|
||||||
>;
|
>;
|
||||||
|
|
||||||
class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
||||||
@@ -480,20 +308,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
),
|
),
|
||||||
defaultValue: const i4.Constant(false),
|
defaultValue: const i4.Constant(false),
|
||||||
);
|
);
|
||||||
static const i0.VerificationMeta _linkedRemoteAlbumIdMeta =
|
|
||||||
const i0.VerificationMeta('linkedRemoteAlbumId');
|
|
||||||
@override
|
|
||||||
late final i0.GeneratedColumn<String> linkedRemoteAlbumId =
|
|
||||||
i0.GeneratedColumn<String>(
|
|
||||||
'linked_remote_album_id',
|
|
||||||
aliasedName,
|
|
||||||
true,
|
|
||||||
type: i0.DriftSqlType.string,
|
|
||||||
requiredDuringInsert: false,
|
|
||||||
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_album_entity (id) ON DELETE SET NULL',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta(
|
static const i0.VerificationMeta _marker_Meta = const i0.VerificationMeta(
|
||||||
'marker_',
|
'marker_',
|
||||||
);
|
);
|
||||||
@@ -515,7 +329,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
updatedAt,
|
updatedAt,
|
||||||
backupSelection,
|
backupSelection,
|
||||||
isIosSharedAlbum,
|
isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId,
|
|
||||||
marker_,
|
marker_,
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
@@ -558,15 +371,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (data.containsKey('linked_remote_album_id')) {
|
|
||||||
context.handle(
|
|
||||||
_linkedRemoteAlbumIdMeta,
|
|
||||||
linkedRemoteAlbumId.isAcceptableOrUnknown(
|
|
||||||
data['linked_remote_album_id']!,
|
|
||||||
_linkedRemoteAlbumIdMeta,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (data.containsKey('marker')) {
|
if (data.containsKey('marker')) {
|
||||||
context.handle(
|
context.handle(
|
||||||
_marker_Meta,
|
_marker_Meta,
|
||||||
@@ -608,10 +412,6 @@ class $LocalAlbumEntityTable extends i3.LocalAlbumEntity
|
|||||||
i0.DriftSqlType.bool,
|
i0.DriftSqlType.bool,
|
||||||
data['${effectivePrefix}is_ios_shared_album'],
|
data['${effectivePrefix}is_ios_shared_album'],
|
||||||
)!,
|
)!,
|
||||||
linkedRemoteAlbumId: attachedDatabase.typeMapping.read(
|
|
||||||
i0.DriftSqlType.string,
|
|
||||||
data['${effectivePrefix}linked_remote_album_id'],
|
|
||||||
),
|
|
||||||
marker_: attachedDatabase.typeMapping.read(
|
marker_: attachedDatabase.typeMapping.read(
|
||||||
i0.DriftSqlType.bool,
|
i0.DriftSqlType.bool,
|
||||||
data['${effectivePrefix}marker'],
|
data['${effectivePrefix}marker'],
|
||||||
@@ -641,7 +441,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
final i2.BackupSelection backupSelection;
|
final i2.BackupSelection backupSelection;
|
||||||
final bool isIosSharedAlbum;
|
final bool isIosSharedAlbum;
|
||||||
final String? linkedRemoteAlbumId;
|
|
||||||
final bool? marker_;
|
final bool? marker_;
|
||||||
const LocalAlbumEntityData({
|
const LocalAlbumEntityData({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -649,7 +448,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.backupSelection,
|
required this.backupSelection,
|
||||||
required this.isIosSharedAlbum,
|
required this.isIosSharedAlbum,
|
||||||
this.linkedRemoteAlbumId,
|
|
||||||
this.marker_,
|
this.marker_,
|
||||||
});
|
});
|
||||||
@override
|
@override
|
||||||
@@ -666,9 +464,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum);
|
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum);
|
||||||
if (!nullToAbsent || linkedRemoteAlbumId != null) {
|
|
||||||
map['linked_remote_album_id'] = i0.Variable<String>(linkedRemoteAlbumId);
|
|
||||||
}
|
|
||||||
if (!nullToAbsent || marker_ != null) {
|
if (!nullToAbsent || marker_ != null) {
|
||||||
map['marker'] = i0.Variable<bool>(marker_);
|
map['marker'] = i0.Variable<bool>(marker_);
|
||||||
}
|
}
|
||||||
@@ -687,9 +482,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
backupSelection: i1.$LocalAlbumEntityTable.$converterbackupSelection
|
||||||
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
|
.fromJson(serializer.fromJson<int>(json['backupSelection'])),
|
||||||
isIosSharedAlbum: serializer.fromJson<bool>(json['isIosSharedAlbum']),
|
isIosSharedAlbum: serializer.fromJson<bool>(json['isIosSharedAlbum']),
|
||||||
linkedRemoteAlbumId: serializer.fromJson<String?>(
|
|
||||||
json['linkedRemoteAlbumId'],
|
|
||||||
),
|
|
||||||
marker_: serializer.fromJson<bool?>(json['marker_']),
|
marker_: serializer.fromJson<bool?>(json['marker_']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -706,7 +498,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
'isIosSharedAlbum': serializer.toJson<bool>(isIosSharedAlbum),
|
'isIosSharedAlbum': serializer.toJson<bool>(isIosSharedAlbum),
|
||||||
'linkedRemoteAlbumId': serializer.toJson<String?>(linkedRemoteAlbumId),
|
|
||||||
'marker_': serializer.toJson<bool?>(marker_),
|
'marker_': serializer.toJson<bool?>(marker_),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -717,7 +508,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
DateTime? updatedAt,
|
DateTime? updatedAt,
|
||||||
i2.BackupSelection? backupSelection,
|
i2.BackupSelection? backupSelection,
|
||||||
bool? isIosSharedAlbum,
|
bool? isIosSharedAlbum,
|
||||||
i0.Value<String?> linkedRemoteAlbumId = const i0.Value.absent(),
|
|
||||||
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
i0.Value<bool?> marker_ = const i0.Value.absent(),
|
||||||
}) => i1.LocalAlbumEntityData(
|
}) => i1.LocalAlbumEntityData(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
||||||
@@ -725,9 +515,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId.present
|
|
||||||
? linkedRemoteAlbumId.value
|
|
||||||
: this.linkedRemoteAlbumId,
|
|
||||||
marker_: marker_.present ? marker_.value : this.marker_,
|
marker_: marker_.present ? marker_.value : this.marker_,
|
||||||
);
|
);
|
||||||
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
|
LocalAlbumEntityData copyWithCompanion(i1.LocalAlbumEntityCompanion data) {
|
||||||
@@ -741,9 +528,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
isIosSharedAlbum: data.isIosSharedAlbum.present
|
isIosSharedAlbum: data.isIosSharedAlbum.present
|
||||||
? data.isIosSharedAlbum.value
|
? data.isIosSharedAlbum.value
|
||||||
: this.isIosSharedAlbum,
|
: this.isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: data.linkedRemoteAlbumId.present
|
|
||||||
? data.linkedRemoteAlbumId.value
|
|
||||||
: this.linkedRemoteAlbumId,
|
|
||||||
marker_: data.marker_.present ? data.marker_.value : this.marker_,
|
marker_: data.marker_.present ? data.marker_.value : this.marker_,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -756,7 +540,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
..write('updatedAt: $updatedAt, ')
|
..write('updatedAt: $updatedAt, ')
|
||||||
..write('backupSelection: $backupSelection, ')
|
..write('backupSelection: $backupSelection, ')
|
||||||
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||||
..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ')
|
|
||||||
..write('marker_: $marker_')
|
..write('marker_: $marker_')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
@@ -769,7 +552,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
updatedAt,
|
updatedAt,
|
||||||
backupSelection,
|
backupSelection,
|
||||||
isIosSharedAlbum,
|
isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId,
|
|
||||||
marker_,
|
marker_,
|
||||||
);
|
);
|
||||||
@override
|
@override
|
||||||
@@ -781,7 +563,6 @@ class LocalAlbumEntityData extends i0.DataClass
|
|||||||
other.updatedAt == this.updatedAt &&
|
other.updatedAt == this.updatedAt &&
|
||||||
other.backupSelection == this.backupSelection &&
|
other.backupSelection == this.backupSelection &&
|
||||||
other.isIosSharedAlbum == this.isIosSharedAlbum &&
|
other.isIosSharedAlbum == this.isIosSharedAlbum &&
|
||||||
other.linkedRemoteAlbumId == this.linkedRemoteAlbumId &&
|
|
||||||
other.marker_ == this.marker_);
|
other.marker_ == this.marker_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -792,7 +573,6 @@ class LocalAlbumEntityCompanion
|
|||||||
final i0.Value<DateTime> updatedAt;
|
final i0.Value<DateTime> updatedAt;
|
||||||
final i0.Value<i2.BackupSelection> backupSelection;
|
final i0.Value<i2.BackupSelection> backupSelection;
|
||||||
final i0.Value<bool> isIosSharedAlbum;
|
final i0.Value<bool> isIosSharedAlbum;
|
||||||
final i0.Value<String?> linkedRemoteAlbumId;
|
|
||||||
final i0.Value<bool?> marker_;
|
final i0.Value<bool?> marker_;
|
||||||
const LocalAlbumEntityCompanion({
|
const LocalAlbumEntityCompanion({
|
||||||
this.id = const i0.Value.absent(),
|
this.id = const i0.Value.absent(),
|
||||||
@@ -800,7 +580,6 @@ class LocalAlbumEntityCompanion
|
|||||||
this.updatedAt = const i0.Value.absent(),
|
this.updatedAt = const i0.Value.absent(),
|
||||||
this.backupSelection = const i0.Value.absent(),
|
this.backupSelection = const i0.Value.absent(),
|
||||||
this.isIosSharedAlbum = const i0.Value.absent(),
|
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||||
this.linkedRemoteAlbumId = const i0.Value.absent(),
|
|
||||||
this.marker_ = const i0.Value.absent(),
|
this.marker_ = const i0.Value.absent(),
|
||||||
});
|
});
|
||||||
LocalAlbumEntityCompanion.insert({
|
LocalAlbumEntityCompanion.insert({
|
||||||
@@ -809,7 +588,6 @@ class LocalAlbumEntityCompanion
|
|||||||
this.updatedAt = const i0.Value.absent(),
|
this.updatedAt = const i0.Value.absent(),
|
||||||
required i2.BackupSelection backupSelection,
|
required i2.BackupSelection backupSelection,
|
||||||
this.isIosSharedAlbum = const i0.Value.absent(),
|
this.isIosSharedAlbum = const i0.Value.absent(),
|
||||||
this.linkedRemoteAlbumId = const i0.Value.absent(),
|
|
||||||
this.marker_ = const i0.Value.absent(),
|
this.marker_ = const i0.Value.absent(),
|
||||||
}) : id = i0.Value(id),
|
}) : id = i0.Value(id),
|
||||||
name = i0.Value(name),
|
name = i0.Value(name),
|
||||||
@@ -820,7 +598,6 @@ class LocalAlbumEntityCompanion
|
|||||||
i0.Expression<DateTime>? updatedAt,
|
i0.Expression<DateTime>? updatedAt,
|
||||||
i0.Expression<int>? backupSelection,
|
i0.Expression<int>? backupSelection,
|
||||||
i0.Expression<bool>? isIosSharedAlbum,
|
i0.Expression<bool>? isIosSharedAlbum,
|
||||||
i0.Expression<String>? linkedRemoteAlbumId,
|
|
||||||
i0.Expression<bool>? marker_,
|
i0.Expression<bool>? marker_,
|
||||||
}) {
|
}) {
|
||||||
return i0.RawValuesInsertable({
|
return i0.RawValuesInsertable({
|
||||||
@@ -829,8 +606,6 @@ class LocalAlbumEntityCompanion
|
|||||||
if (updatedAt != null) 'updated_at': updatedAt,
|
if (updatedAt != null) 'updated_at': updatedAt,
|
||||||
if (backupSelection != null) 'backup_selection': backupSelection,
|
if (backupSelection != null) 'backup_selection': backupSelection,
|
||||||
if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum,
|
if (isIosSharedAlbum != null) 'is_ios_shared_album': isIosSharedAlbum,
|
||||||
if (linkedRemoteAlbumId != null)
|
|
||||||
'linked_remote_album_id': linkedRemoteAlbumId,
|
|
||||||
if (marker_ != null) 'marker': marker_,
|
if (marker_ != null) 'marker': marker_,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -841,7 +616,6 @@ class LocalAlbumEntityCompanion
|
|||||||
i0.Value<DateTime>? updatedAt,
|
i0.Value<DateTime>? updatedAt,
|
||||||
i0.Value<i2.BackupSelection>? backupSelection,
|
i0.Value<i2.BackupSelection>? backupSelection,
|
||||||
i0.Value<bool>? isIosSharedAlbum,
|
i0.Value<bool>? isIosSharedAlbum,
|
||||||
i0.Value<String?>? linkedRemoteAlbumId,
|
|
||||||
i0.Value<bool?>? marker_,
|
i0.Value<bool?>? marker_,
|
||||||
}) {
|
}) {
|
||||||
return i1.LocalAlbumEntityCompanion(
|
return i1.LocalAlbumEntityCompanion(
|
||||||
@@ -850,7 +624,6 @@ class LocalAlbumEntityCompanion
|
|||||||
updatedAt: updatedAt ?? this.updatedAt,
|
updatedAt: updatedAt ?? this.updatedAt,
|
||||||
backupSelection: backupSelection ?? this.backupSelection,
|
backupSelection: backupSelection ?? this.backupSelection,
|
||||||
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
isIosSharedAlbum: isIosSharedAlbum ?? this.isIosSharedAlbum,
|
||||||
linkedRemoteAlbumId: linkedRemoteAlbumId ?? this.linkedRemoteAlbumId,
|
|
||||||
marker_: marker_ ?? this.marker_,
|
marker_: marker_ ?? this.marker_,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -877,11 +650,6 @@ class LocalAlbumEntityCompanion
|
|||||||
if (isIosSharedAlbum.present) {
|
if (isIosSharedAlbum.present) {
|
||||||
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum.value);
|
map['is_ios_shared_album'] = i0.Variable<bool>(isIosSharedAlbum.value);
|
||||||
}
|
}
|
||||||
if (linkedRemoteAlbumId.present) {
|
|
||||||
map['linked_remote_album_id'] = i0.Variable<String>(
|
|
||||||
linkedRemoteAlbumId.value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (marker_.present) {
|
if (marker_.present) {
|
||||||
map['marker'] = i0.Variable<bool>(marker_.value);
|
map['marker'] = i0.Variable<bool>(marker_.value);
|
||||||
}
|
}
|
||||||
@@ -896,7 +664,6 @@ class LocalAlbumEntityCompanion
|
|||||||
..write('updatedAt: $updatedAt, ')
|
..write('updatedAt: $updatedAt, ')
|
||||||
..write('backupSelection: $backupSelection, ')
|
..write('backupSelection: $backupSelection, ')
|
||||||
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
..write('isIosSharedAlbum: $isIosSharedAlbum, ')
|
||||||
..write('linkedRemoteAlbumId: $linkedRemoteAlbumId, ')
|
|
||||||
..write('marker_: $marker_')
|
..write('marker_: $marker_')
|
||||||
..write(')'))
|
..write(')'))
|
||||||
.toString();
|
.toString();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin {
|
|||||||
Set<Column> get primaryKey => {id};
|
Set<Column> get primaryKey => {id};
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LocalAssetEntityDataDomainExtension on LocalAssetEntityData {
|
extension LocalAssetEntityDataDomainEx on LocalAssetEntityData {
|
||||||
LocalAsset toDto() => LocalAsset(
|
LocalAsset toDto() => LocalAsset(
|
||||||
id: id,
|
id: id,
|
||||||
name: name,
|
name: name,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import 'package:drift/drift.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.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/db.repository.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
import "package:immich_mobile/utils/database.utils.dart";
|
||||||
|
|
||||||
final backupRepositoryProvider = Provider<DriftBackupRepository>(
|
final backupRepositoryProvider = Provider<DriftBackupRepository>(
|
||||||
(ref) => DriftBackupRepository(ref.watch(driftProvider)),
|
(ref) => DriftBackupRepository(ref.watch(driftProvider)),
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||||||
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
|
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 9;
|
int get schemaVersion => 8;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
@@ -123,9 +123,6 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||||||
from7To8: (m, v8) async {
|
from7To8: (m, v8) async {
|
||||||
await m.create(v8.storeEntity);
|
await m.create(v8.storeEntity);
|
||||||
},
|
},
|
||||||
from8To9: (m, v9) async {
|
|
||||||
await m.addColumn(v9.localAlbumEntity, v9.localAlbumEntity.linkedRemoteAlbumId);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+44
-53
@@ -9,17 +9,17 @@ import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'
|
|||||||
as i3;
|
as i3;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart'
|
||||||
as i4;
|
as i4;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
|
||||||
as i5;
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart'
|
||||||
as i6;
|
as i5;
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart'
|
||||||
as i7;
|
as i6;
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.drift.dart'
|
||||||
as i8;
|
as i7;
|
||||||
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'
|
||||||
as i9;
|
as i8;
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
|
||||||
|
as i9;
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
|
||||||
as i10;
|
as i10;
|
||||||
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
|
||||||
as i11;
|
as i11;
|
||||||
@@ -48,19 +48,19 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this);
|
late final i3.$StackEntityTable stackEntity = i3.$StackEntityTable(this);
|
||||||
late final i4.$LocalAssetEntityTable localAssetEntity = i4
|
late final i4.$LocalAssetEntityTable localAssetEntity = i4
|
||||||
.$LocalAssetEntityTable(this);
|
.$LocalAssetEntityTable(this);
|
||||||
late final i5.$RemoteAlbumEntityTable remoteAlbumEntity = i5
|
late final i5.$LocalAlbumEntityTable localAlbumEntity = i5
|
||||||
.$RemoteAlbumEntityTable(this);
|
|
||||||
late final i6.$LocalAlbumEntityTable localAlbumEntity = i6
|
|
||||||
.$LocalAlbumEntityTable(this);
|
.$LocalAlbumEntityTable(this);
|
||||||
late final i7.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i7
|
late final i6.$LocalAlbumAssetEntityTable localAlbumAssetEntity = i6
|
||||||
.$LocalAlbumAssetEntityTable(this);
|
.$LocalAlbumAssetEntityTable(this);
|
||||||
late final i8.$UserMetadataEntityTable userMetadataEntity = i8
|
late final i7.$UserMetadataEntityTable userMetadataEntity = i7
|
||||||
.$UserMetadataEntityTable(this);
|
.$UserMetadataEntityTable(this);
|
||||||
late final i9.$PartnerEntityTable partnerEntity = i9.$PartnerEntityTable(
|
late final i8.$PartnerEntityTable partnerEntity = i8.$PartnerEntityTable(
|
||||||
this,
|
this,
|
||||||
);
|
);
|
||||||
late final i10.$RemoteExifEntityTable remoteExifEntity = i10
|
late final i9.$RemoteExifEntityTable remoteExifEntity = i9
|
||||||
.$RemoteExifEntityTable(this);
|
.$RemoteExifEntityTable(this);
|
||||||
|
late final i10.$RemoteAlbumEntityTable remoteAlbumEntity = i10
|
||||||
|
.$RemoteAlbumEntityTable(this);
|
||||||
late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i11
|
late final i11.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity = i11
|
||||||
.$RemoteAlbumAssetEntityTable(this);
|
.$RemoteAlbumAssetEntityTable(this);
|
||||||
late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i12
|
late final i12.$RemoteAlbumUserEntityTable remoteAlbumUserEntity = i12
|
||||||
@@ -84,7 +84,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
remoteAssetEntity,
|
remoteAssetEntity,
|
||||||
stackEntity,
|
stackEntity,
|
||||||
localAssetEntity,
|
localAssetEntity,
|
||||||
remoteAlbumEntity,
|
|
||||||
localAlbumEntity,
|
localAlbumEntity,
|
||||||
localAlbumAssetEntity,
|
localAlbumAssetEntity,
|
||||||
i4.idxLocalAssetChecksum,
|
i4.idxLocalAssetChecksum,
|
||||||
@@ -95,6 +94,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
userMetadataEntity,
|
userMetadataEntity,
|
||||||
partnerEntity,
|
partnerEntity,
|
||||||
remoteExifEntity,
|
remoteExifEntity,
|
||||||
|
remoteAlbumEntity,
|
||||||
remoteAlbumAssetEntity,
|
remoteAlbumAssetEntity,
|
||||||
remoteAlbumUserEntity,
|
remoteAlbumUserEntity,
|
||||||
memoryEntity,
|
memoryEntity,
|
||||||
@@ -102,7 +102,7 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
personEntity,
|
personEntity,
|
||||||
assetFaceEntity,
|
assetFaceEntity,
|
||||||
storeEntity,
|
storeEntity,
|
||||||
i10.idxLatLng,
|
i9.idxLatLng,
|
||||||
];
|
];
|
||||||
@override
|
@override
|
||||||
i0.StreamQueryUpdateRules
|
i0.StreamQueryUpdateRules
|
||||||
@@ -123,33 +123,6 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
),
|
),
|
||||||
result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)],
|
result: [i0.TableUpdate('stack_entity', kind: i0.UpdateKind.delete)],
|
||||||
),
|
),
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
|
||||||
'user_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete,
|
|
||||||
),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
|
||||||
'remote_asset_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete,
|
|
||||||
),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
|
||||||
'remote_album_entity',
|
|
||||||
limitUpdateKind: i0.UpdateKind.delete,
|
|
||||||
),
|
|
||||||
result: [
|
|
||||||
i0.TableUpdate('local_album_entity', kind: i0.UpdateKind.update),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
i0.WritePropagation(
|
i0.WritePropagation(
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
'local_asset_entity',
|
'local_asset_entity',
|
||||||
@@ -200,6 +173,24 @@ abstract class $Drift extends i0.GeneratedDatabase {
|
|||||||
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
|
i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
|
'user_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete,
|
||||||
|
),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
i0.WritePropagation(
|
||||||
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
|
'remote_asset_entity',
|
||||||
|
limitUpdateKind: i0.UpdateKind.delete,
|
||||||
|
),
|
||||||
|
result: [
|
||||||
|
i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
|
||||||
|
],
|
||||||
|
),
|
||||||
i0.WritePropagation(
|
i0.WritePropagation(
|
||||||
on: i0.TableUpdateQuery.onTableName(
|
on: i0.TableUpdateQuery.onTableName(
|
||||||
'remote_asset_entity',
|
'remote_asset_entity',
|
||||||
@@ -299,18 +290,18 @@ class $DriftManager {
|
|||||||
i3.$$StackEntityTableTableManager(_db, _db.stackEntity);
|
i3.$$StackEntityTableTableManager(_db, _db.stackEntity);
|
||||||
i4.$$LocalAssetEntityTableTableManager get localAssetEntity =>
|
i4.$$LocalAssetEntityTableTableManager get localAssetEntity =>
|
||||||
i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
i4.$$LocalAssetEntityTableTableManager(_db, _db.localAssetEntity);
|
||||||
i5.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
|
i5.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
|
||||||
i5.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
|
i5.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
|
||||||
i6.$$LocalAlbumEntityTableTableManager get localAlbumEntity =>
|
i6.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i6
|
||||||
i6.$$LocalAlbumEntityTableTableManager(_db, _db.localAlbumEntity);
|
|
||||||
i7.$$LocalAlbumAssetEntityTableTableManager get localAlbumAssetEntity => i7
|
|
||||||
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
.$$LocalAlbumAssetEntityTableTableManager(_db, _db.localAlbumAssetEntity);
|
||||||
i8.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
|
i7.$$UserMetadataEntityTableTableManager get userMetadataEntity =>
|
||||||
i8.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
|
i7.$$UserMetadataEntityTableTableManager(_db, _db.userMetadataEntity);
|
||||||
i9.$$PartnerEntityTableTableManager get partnerEntity =>
|
i8.$$PartnerEntityTableTableManager get partnerEntity =>
|
||||||
i9.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
|
i8.$$PartnerEntityTableTableManager(_db, _db.partnerEntity);
|
||||||
i10.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
i9.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
|
||||||
i10.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
i9.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
|
||||||
|
i10.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
|
||||||
|
i10.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
|
||||||
i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
|
i11.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
|
||||||
i11.$$RemoteAlbumAssetEntityTableTableManager(
|
i11.$$RemoteAlbumAssetEntityTableTableManager(
|
||||||
_db,
|
_db,
|
||||||
|
|||||||
@@ -3435,391 +3435,6 @@ i1.GeneratedColumn<int> _column_89(String aliasedName) =>
|
|||||||
true,
|
true,
|
||||||
type: i1.DriftSqlType.int,
|
type: i1.DriftSqlType.int,
|
||||||
);
|
);
|
||||||
|
|
||||||
final class Schema9 extends i0.VersionedSchema {
|
|
||||||
Schema9({required super.database}) : super(version: 9);
|
|
||||||
@override
|
|
||||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
|
||||||
userEntity,
|
|
||||||
remoteAssetEntity,
|
|
||||||
stackEntity,
|
|
||||||
localAssetEntity,
|
|
||||||
remoteAlbumEntity,
|
|
||||||
localAlbumEntity,
|
|
||||||
localAlbumAssetEntity,
|
|
||||||
idxLocalAssetChecksum,
|
|
||||||
idxRemoteAssetOwnerChecksum,
|
|
||||||
uQRemoteAssetsOwnerChecksum,
|
|
||||||
uQRemoteAssetsOwnerLibraryChecksum,
|
|
||||||
idxRemoteAssetChecksum,
|
|
||||||
userMetadataEntity,
|
|
||||||
partnerEntity,
|
|
||||||
remoteExifEntity,
|
|
||||||
remoteAlbumAssetEntity,
|
|
||||||
remoteAlbumUserEntity,
|
|
||||||
memoryEntity,
|
|
||||||
memoryAssetEntity,
|
|
||||||
personEntity,
|
|
||||||
assetFaceEntity,
|
|
||||||
storeEntity,
|
|
||||||
idxLatLng,
|
|
||||||
];
|
|
||||||
late final Shape16 userEntity = Shape16(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'user_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: ['PRIMARY KEY(id)'],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_1,
|
|
||||||
_column_2,
|
|
||||||
_column_3,
|
|
||||||
_column_84,
|
|
||||||
_column_85,
|
|
||||||
_column_5,
|
|
||||||
],
|
|
||||||
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 Shape2 localAssetEntity = Shape2(
|
|
||||||
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,
|
|
||||||
],
|
|
||||||
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 Shape7 localAlbumAssetEntity = Shape7(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'local_album_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
|
|
||||||
columns: [_column_34, _column_35],
|
|
||||||
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 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 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 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,
|
|
||||||
);
|
|
||||||
final i1.Index idxLatLng = i1.Index(
|
|
||||||
'idx_lat_lng',
|
|
||||||
'CREATE INDEX IF NOT EXISTS idx_lat_lng ON remote_exif_entity (latitude, longitude)',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Shape19 extends i0.VersionedTable {
|
|
||||||
Shape19({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<int> get backupSelection =>
|
|
||||||
columnsByName['backup_selection']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<bool> get isIosSharedAlbum =>
|
|
||||||
columnsByName['is_ios_shared_album']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<String> get linkedRemoteAlbumId =>
|
|
||||||
columnsByName['linked_remote_album_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get marker_ =>
|
|
||||||
columnsByName['marker']! as i1.GeneratedColumn<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_90(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>(
|
|
||||||
'linked_remote_album_id',
|
|
||||||
aliasedName,
|
|
||||||
true,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_album_entity (id) ON DELETE SET NULL',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
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,
|
||||||
@@ -3828,7 +3443,6 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||||||
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
||||||
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
||||||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
|
||||||
}) {
|
}) {
|
||||||
return (currentVersion, database) async {
|
return (currentVersion, database) async {
|
||||||
switch (currentVersion) {
|
switch (currentVersion) {
|
||||||
@@ -3867,11 +3481,6 @@ i0.MigrationStepWithVersion migrationSteps({
|
|||||||
final migrator = i1.Migrator(database, schema);
|
final migrator = i1.Migrator(database, schema);
|
||||||
await from7To8(migrator, schema);
|
await from7To8(migrator, schema);
|
||||||
return 8;
|
return 8;
|
||||||
case 8:
|
|
||||||
final schema = Schema9(database: database);
|
|
||||||
final migrator = i1.Migrator(database, schema);
|
|
||||||
await from8To9(migrator, schema);
|
|
||||||
return 9;
|
|
||||||
default:
|
default:
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
throw ArgumentError.value('Unknown migration from $currentVersion');
|
||||||
}
|
}
|
||||||
@@ -3886,7 +3495,6 @@ i1.OnUpgrade stepByStep({
|
|||||||
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
required Future<void> Function(i1.Migrator m, Schema6 schema) from5To6,
|
||||||
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
required Future<void> Function(i1.Migrator m, Schema7 schema) from6To7,
|
||||||
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
required Future<void> Function(i1.Migrator m, Schema8 schema) from7To8,
|
||||||
required Future<void> Function(i1.Migrator m, Schema9 schema) from8To9,
|
|
||||||
}) => i0.VersionedSchema.stepByStepHelper(
|
}) => i0.VersionedSchema.stepByStepHelper(
|
||||||
step: migrationSteps(
|
step: migrationSteps(
|
||||||
from1To2: from1To2,
|
from1To2: from1To2,
|
||||||
@@ -3896,6 +3504,5 @@ i1.OnUpgrade stepByStep({
|
|||||||
from5To6: from5To6,
|
from5To6: from5To6,
|
||||||
from6To7: from6To7,
|
from6To7: from6To7,
|
||||||
from7To8: from7To8,
|
from7To8: from7To8,
|
||||||
from8To9: from8To9,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
|
import 'package:immich_mobile/utils/database.utils.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
|
|
||||||
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum, name, assetCount, newestAsset }
|
enum SortLocalAlbumsBy { id, backupSelection, isIosSharedAlbum, name, assetCount, newestAsset }
|
||||||
@@ -50,13 +49,6 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get();
|
return query.map((row) => row.readTable(_db.localAlbumEntity).toDto(assetCount: row.read(assetCount) ?? 0)).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<LocalAlbum>> getBackupAlbums() async {
|
|
||||||
final query = _db.localAlbumEntity.select()
|
|
||||||
..where((row) => row.backupSelection.equalsValue(BackupSelection.selected));
|
|
||||||
|
|
||||||
return query.map((row) => row.toDto()).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> delete(String albumId) => transaction(() async {
|
Future<void> delete(String albumId) => transaction(() async {
|
||||||
// Remove all assets that are only in this particular album
|
// Remove all assets that are only in this particular album
|
||||||
// We cannot remove all assets in the album because they might be in other albums in iOS
|
// We cannot remove all assets in the album because they might be in other albums in iOS
|
||||||
@@ -343,16 +335,4 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
Future<int> getCount() {
|
Future<int> getCount() {
|
||||||
return _db.managers.localAlbumEntity.count();
|
return _db.managers.localAlbumEntity.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future unlinkRemoteAlbum(String id) async {
|
|
||||||
return _db.localAlbumEntity.update()
|
|
||||||
..where((row) => row.id.equals(id))
|
|
||||||
..write(const LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(null)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future linkRemoteAlbum(String localAlbumId, String remoteAlbumId) async {
|
|
||||||
return _db.localAlbumEntity.update()
|
|
||||||
..where((row) => row.id.equals(localAlbumId))
|
|
||||||
..write(LocalAlbumEntityCompanion(linkedRemoteAlbumId: Value(remoteAlbumId)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,15 +113,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<RemoteAlbum?> getByName(String albumName, String ownerId) {
|
|
||||||
final query = _db.remoteAlbumEntity.select()
|
|
||||||
..where((row) => row.name.equals(albumName) & row.ownerId.equals(ownerId))
|
|
||||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
|
||||||
..limit(1);
|
|
||||||
|
|
||||||
return query.map((row) => row.toDto(ownerName: '', isShared: false)).getSingleOrNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> create(RemoteAlbum album, List<String> assetIds) async {
|
Future<void> create(RemoteAlbum album, List<String> assetIds) async {
|
||||||
await _db.transaction(() async {
|
await _db.transaction(() async {
|
||||||
final entity = RemoteAlbumEntityCompanion(
|
final entity = RemoteAlbumEntityCompanion(
|
||||||
@@ -330,42 +321,6 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
Future<int> getCount() {
|
Future<int> getCount() {
|
||||||
return _db.managers.remoteAlbumEntity.count();
|
return _db.managers.remoteAlbumEntity.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<String>> getLinkedAssetIds(String userId, String localAlbumId, String remoteAlbumId) async {
|
|
||||||
// Find remote asset ids that:
|
|
||||||
// 1. Belong to the provided local album (via local_album_asset_entity)
|
|
||||||
// 2. Have been uploaded (i.e. a matching remote asset exists for the same checksum & owner)
|
|
||||||
// 3. Are NOT already in the remote album (remote_album_asset_entity)
|
|
||||||
final query = _db.remoteAssetEntity.selectOnly()
|
|
||||||
..addColumns([_db.remoteAssetEntity.id])
|
|
||||||
..join([
|
|
||||||
innerJoin(
|
|
||||||
_db.localAssetEntity,
|
|
||||||
_db.remoteAssetEntity.checksum.equalsExp(_db.localAssetEntity.checksum),
|
|
||||||
useColumns: false,
|
|
||||||
),
|
|
||||||
innerJoin(
|
|
||||||
_db.localAlbumAssetEntity,
|
|
||||||
_db.localAlbumAssetEntity.assetId.equalsExp(_db.localAssetEntity.id),
|
|
||||||
useColumns: false,
|
|
||||||
),
|
|
||||||
// Left join remote album assets to exclude those already in the remote album
|
|
||||||
leftOuterJoin(
|
|
||||||
_db.remoteAlbumAssetEntity,
|
|
||||||
_db.remoteAlbumAssetEntity.assetId.equalsExp(_db.remoteAssetEntity.id) &
|
|
||||||
_db.remoteAlbumAssetEntity.albumId.equals(remoteAlbumId),
|
|
||||||
useColumns: false,
|
|
||||||
),
|
|
||||||
])
|
|
||||||
..where(
|
|
||||||
_db.remoteAssetEntity.ownerId.equals(userId) &
|
|
||||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
|
||||||
_db.localAlbumAssetEntity.albumId.equals(localAlbumId) &
|
|
||||||
_db.remoteAlbumAssetEntity.assetId.isNull(), // only those not yet linked
|
|
||||||
);
|
|
||||||
|
|
||||||
return query.map((row) => row.read(_db.remoteAssetEntity.id)!).get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on RemoteAlbumEntityData {
|
extension on RemoteAlbumEntityData {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup_album.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/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
@@ -27,10 +26,10 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
String _searchQuery = '';
|
String _searchQuery = '';
|
||||||
bool _isSearchMode = false;
|
bool _isSearchMode = false;
|
||||||
int _initialTotalAssetCount = 0;
|
int _initialTotalAssetCount = 0;
|
||||||
|
bool _hasPopped = false;
|
||||||
late ValueNotifier<bool> _enableSyncUploadAlbum;
|
late ValueNotifier<bool> _enableSyncUploadAlbum;
|
||||||
late TextEditingController _searchController;
|
late TextEditingController _searchController;
|
||||||
late FocusNode _searchFocusNode;
|
late FocusNode _searchFocusNode;
|
||||||
Future? _handleLinkedAlbumFuture;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -45,36 +44,6 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handlePagePopped() async {
|
|
||||||
final user = ref.read(currentUserProvider);
|
|
||||||
if (user == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final enableSyncUploadAlbum = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
|
||||||
final selectedAlbums = ref
|
|
||||||
.read(backupAlbumProvider)
|
|
||||||
.where((a) => a.backupSelection == BackupSelection.selected)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
if (enableSyncUploadAlbum && selectedAlbums.isNotEmpty) {
|
|
||||||
setState(() {
|
|
||||||
_handleLinkedAlbumFuture = ref.read(syncLinkedAlbumServiceProvider).manageLinkedAlbums(selectedAlbums, user.id);
|
|
||||||
});
|
|
||||||
await _handleLinkedAlbumFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart backup if total count changed and backup is enabled
|
|
||||||
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
|
||||||
final totalChanged = currentTotalAssetCount != _initialTotalAssetCount;
|
|
||||||
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
|
||||||
|
|
||||||
if (totalChanged && isBackupEnabled) {
|
|
||||||
await ref.read(driftBackupProvider.notifier).cancel();
|
|
||||||
await ref.read(driftBackupProvider.notifier).startBackup(user.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_enableSyncUploadAlbum.dispose();
|
_enableSyncUploadAlbum.dispose();
|
||||||
@@ -96,12 +65,42 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).toList();
|
final selectedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.selected).toList();
|
||||||
final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList();
|
final excludedBackupAlbums = albums.where((album) => album.backupSelection == BackupSelection.excluded).toList();
|
||||||
|
|
||||||
|
// handleSyncAlbumToggle(bool isEnable) async {
|
||||||
|
// if (isEnable) {
|
||||||
|
// await ref.read(albumProvider.notifier).refreshRemoteAlbums();
|
||||||
|
// for (final album in selectedBackupAlbums) {
|
||||||
|
// await ref.read(albumProvider.notifier).createSyncAlbum(album.name);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
onPopInvokedWithResult: (didPop, result) async {
|
||||||
onPopInvokedWithResult: (didPop, _) async {
|
// There is an issue with Flutter where the pop event
|
||||||
if (!didPop) {
|
// can be triggered multiple times, so we guard it with _hasPopped
|
||||||
await _handlePagePopped();
|
if (didPop && !_hasPopped) {
|
||||||
Navigator.of(context).pop();
|
_hasPopped = true;
|
||||||
|
|
||||||
|
final currentUser = ref.read(currentUserProvider);
|
||||||
|
if (currentUser == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ref.read(driftBackupProvider.notifier).getBackupStatus(currentUser.id);
|
||||||
|
final currentTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
|
||||||
|
|
||||||
|
if (currentTotalAssetCount != _initialTotalAssetCount) {
|
||||||
|
final isBackupEnabled = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
||||||
|
|
||||||
|
if (!isBackupEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final backupNotifier = ref.read(driftBackupProvider.notifier);
|
||||||
|
|
||||||
|
backupNotifier.cancel().then((_) {
|
||||||
|
backupNotifier.startBackup(currentUser.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
@@ -140,123 +139,103 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
|
|||||||
],
|
],
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: CustomScrollView(
|
||||||
children: [
|
physics: const ClampingScrollPhysics(),
|
||||||
CustomScrollView(
|
slivers: [
|
||||||
physics: const ClampingScrollPhysics(),
|
SliverToBoxAdapter(
|
||||||
slivers: [
|
child: Column(
|
||||||
SliverToBoxAdapter(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: Column(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
Padding(
|
||||||
children: [
|
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
||||||
Padding(
|
child: Text(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
|
"backup_album_selection_page_selection_info",
|
||||||
child: Text(
|
style: context.textTheme.titleSmall,
|
||||||
"backup_album_selection_page_selection_info",
|
).t(context: context),
|
||||||
style: context.textTheme.titleSmall,
|
),
|
||||||
).t(context: context),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Selected Album Chips
|
// Selected Album Chips
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
children: [
|
children: [
|
||||||
_SelectedAlbumNameChips(selectedBackupAlbums: selectedBackupAlbums),
|
_SelectedAlbumNameChips(selectedBackupAlbums: selectedBackupAlbums),
|
||||||
_ExcludedAlbumNameChips(excludedBackupAlbums: excludedBackupAlbums),
|
_ExcludedAlbumNameChips(excludedBackupAlbums: excludedBackupAlbums),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
|
||||||
title: Text(
|
// SettingsSwitchListTile(
|
||||||
"albums_on_device_count".t(context: context, args: {'count': albumCount.toString()}),
|
// valueNotifier: _enableSyncUploadAlbum,
|
||||||
style: context.textTheme.titleSmall,
|
// title: "sync_albums".t(context: context),
|
||||||
),
|
// subtitle: "sync_upload_album_setting_subtitle".t(context: context),
|
||||||
subtitle: Padding(
|
// contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
// titleStyle: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.bold),
|
||||||
child: Text(
|
// subtitleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.primary),
|
||||||
"backup_album_selection_page_albums_tap",
|
// onChanged: handleSyncAlbumToggle,
|
||||||
style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor),
|
// ),
|
||||||
).t(context: context),
|
ListTile(
|
||||||
),
|
title: Text(
|
||||||
trailing: IconButton(
|
"albums_on_device_count".t(context: context, args: {'count': albumCount.toString()}),
|
||||||
splashRadius: 16,
|
style: context.textTheme.titleSmall,
|
||||||
icon: Icon(Icons.info, size: 20, color: context.primaryColor),
|
),
|
||||||
onPressed: () {
|
subtitle: Padding(
|
||||||
showDialog(
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
context: context,
|
child: Text(
|
||||||
builder: (BuildContext context) {
|
"backup_album_selection_page_albums_tap",
|
||||||
return AlertDialog(
|
style: context.textTheme.labelLarge?.copyWith(color: context.primaryColor),
|
||||||
shape: const RoundedRectangleBorder(
|
).t(context: context),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
),
|
||||||
),
|
trailing: IconButton(
|
||||||
elevation: 5,
|
splashRadius: 16,
|
||||||
title: Text(
|
icon: Icon(Icons.info, size: 20, color: context.primaryColor),
|
||||||
'backup_album_selection_page_selection_info',
|
onPressed: () {
|
||||||
style: TextStyle(
|
// show the dialog
|
||||||
fontSize: 16,
|
showDialog(
|
||||||
fontWeight: FontWeight.bold,
|
context: context,
|
||||||
color: context.primaryColor,
|
builder: (BuildContext context) {
|
||||||
),
|
return AlertDialog(
|
||||||
).t(context: context),
|
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
|
||||||
content: SingleChildScrollView(
|
elevation: 5,
|
||||||
child: ListBody(
|
title: Text(
|
||||||
children: [
|
'backup_album_selection_page_selection_info',
|
||||||
const Text(
|
style: TextStyle(
|
||||||
'backup_album_selection_page_assets_scatter',
|
fontSize: 16,
|
||||||
style: TextStyle(fontSize: 14),
|
fontWeight: FontWeight.bold,
|
||||||
).t(context: context),
|
color: context.primaryColor,
|
||||||
],
|
),
|
||||||
),
|
).t(context: context),
|
||||||
),
|
content: SingleChildScrollView(
|
||||||
);
|
child: ListBody(
|
||||||
},
|
children: [
|
||||||
|
const Text(
|
||||||
|
'backup_album_selection_page_assets_scatter',
|
||||||
|
style: TextStyle(fontSize: 14),
|
||||||
|
).t(context: context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
|
|
||||||
if (Platform.isAndroid)
|
|
||||||
_SelectAllButton(filteredAlbums: filteredAlbums, selectedBackupAlbums: selectedBackupAlbums),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SliverLayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
if (constraints.crossAxisExtent > 600) {
|
|
||||||
return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
|
|
||||||
} else {
|
|
||||||
return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (_handleLinkedAlbumFuture != null)
|
|
||||||
FutureBuilder(
|
|
||||||
future: _handleLinkedAlbumFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
return SizedBox(
|
|
||||||
height: double.infinity,
|
|
||||||
width: double.infinity,
|
|
||||||
child: Container(
|
|
||||||
color: context.scaffoldBackgroundColor.withValues(alpha: 0.8),
|
|
||||||
child: Center(
|
|
||||||
child: Column(
|
|
||||||
spacing: 16,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: [
|
|
||||||
const CircularProgressIndicator(strokeWidth: 4),
|
|
||||||
Text("Creating linked albums...", style: context.textTheme.labelLarge),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
|
if (Platform.isAndroid)
|
||||||
|
_SelectAllButton(filteredAlbums: filteredAlbums, selectedBackupAlbums: selectedBackupAlbums),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
SliverLayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
if (constraints.crossAxisExtent > 600) {
|
||||||
|
return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
|
||||||
|
} else {
|
||||||
|
return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -47,12 +47,10 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
|||||||
MapLibreMapController? mapController;
|
MapLibreMapController? mapController;
|
||||||
final _reloadMutex = AsyncMutex();
|
final _reloadMutex = AsyncMutex();
|
||||||
final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2));
|
final _debouncer = Debouncer(interval: const Duration(milliseconds: 500), maxWaitTime: const Duration(seconds: 2));
|
||||||
final ValueNotifier<double> bottomSheetOffset = ValueNotifier(0.25);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_debouncer.dispose();
|
_debouncer.dispose();
|
||||||
bottomSheetOffset.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +157,8 @@ class _DriftMapState extends ConsumerState<DriftMap> {
|
|||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
_Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady),
|
_Map(initialLocation: widget.initialLocation, onMapCreated: onMapCreated, onMapReady: onMapReady),
|
||||||
_DynamicBottomSheet(bottomSheetOffset: bottomSheetOffset),
|
_MyLocationButton(onZoomToLocation: onZoomToLocation),
|
||||||
_DynamicMyLocationButton(onZoomToLocation: onZoomToLocation, bottomSheetOffset: bottomSheetOffset),
|
const MapBottomSheet(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -193,53 +191,21 @@ class _Map extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DynamicBottomSheet extends StatefulWidget {
|
class _MyLocationButton extends StatelessWidget {
|
||||||
final ValueNotifier<double> bottomSheetOffset;
|
const _MyLocationButton({required this.onZoomToLocation});
|
||||||
|
|
||||||
const _DynamicBottomSheet({required this.bottomSheetOffset});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<_DynamicBottomSheet> createState() => _DynamicBottomSheetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DynamicBottomSheetState extends State<_DynamicBottomSheet> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return NotificationListener<DraggableScrollableNotification>(
|
|
||||||
onNotification: (notification) {
|
|
||||||
widget.bottomSheetOffset.value = notification.extent;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
child: const MapBottomSheet(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DynamicMyLocationButton extends StatelessWidget {
|
|
||||||
const _DynamicMyLocationButton({required this.onZoomToLocation, required this.bottomSheetOffset});
|
|
||||||
|
|
||||||
final VoidCallback onZoomToLocation;
|
final VoidCallback onZoomToLocation;
|
||||||
final ValueNotifier<double> bottomSheetOffset;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ValueListenableBuilder<double>(
|
return Positioned(
|
||||||
valueListenable: bottomSheetOffset,
|
right: 0,
|
||||||
builder: (context, offset, child) {
|
bottom: context.padding.bottom + 16,
|
||||||
return Positioned(
|
child: ElevatedButton(
|
||||||
right: 16,
|
onPressed: onZoomToLocation,
|
||||||
bottom: context.height * (offset - 0.02) + context.padding.bottom,
|
style: ElevatedButton.styleFrom(shape: const CircleBorder()),
|
||||||
child: AnimatedOpacity(
|
child: const Icon(Icons.my_location),
|
||||||
opacity: offset < 0.8 ? 1 : 0,
|
),
|
||||||
duration: const Duration(milliseconds: 150),
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: onZoomToLocation,
|
|
||||||
style: ElevatedButton.styleFrom(shape: const CircleBorder()),
|
|
||||||
child: const Icon(Icons.my_location),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,8 +105,6 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
]).then((_) async {
|
]).then((_) async {
|
||||||
final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
final isEnableBackup = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.enableBackup);
|
||||||
|
|
||||||
final isAlbumLinkedSyncEnable = _ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
|
||||||
|
|
||||||
if (isEnableBackup) {
|
if (isEnableBackup) {
|
||||||
final currentUser = _ref.read(currentUserProvider);
|
final currentUser = _ref.read(currentUserProvider);
|
||||||
if (currentUser == null) {
|
if (currentUser == null) {
|
||||||
@@ -115,10 +113,6 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
|
|
||||||
await _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
|
await _ref.read(driftBackupProvider.notifier).handleBackupResume(currentUser.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAlbumLinkedSyncEnable) {
|
|
||||||
await backgroundManager.syncLinkedAlbum();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
Logger("AppLifeCycleNotifier").severe("Error during background sync", e, stackTrace);
|
Logger("AppLifeCycleNotifier").severe("Error during background sync", e, stackTrace);
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:immich_mobile/models/server_info/server_version.model.dart';
|
|||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.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/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/db.provider.dart';
|
import 'package:immich_mobile/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
@@ -322,11 +323,7 @@ class WebsocketNotifier extends StateNotifier<WebsocketState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unawaited(
|
unawaited(_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()));
|
||||||
_ref.read(backgroundSyncProvider).syncWebsocketBatch(_batchedAssetUploadReady.toList()).then((_) {
|
|
||||||
return _ref.read(backgroundSyncProvider).syncLinkedAlbum();
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
_log.severe("Error processing batched AssetUploadReadyV1 events: $error");
|
_log.severe("Error processing batched AssetUploadReadyV1 events: $error");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,8 +282,6 @@ class UploadService {
|
|||||||
|
|
||||||
return buildUploadTask(
|
return buildUploadTask(
|
||||||
file,
|
file,
|
||||||
createdAt: asset.createdAt,
|
|
||||||
modifiedAt: asset.updatedAt,
|
|
||||||
originalFileName: originalFileName,
|
originalFileName: originalFileName,
|
||||||
deviceAssetId: asset.id,
|
deviceAssetId: asset.id,
|
||||||
metadata: metadata,
|
metadata: metadata,
|
||||||
@@ -311,8 +309,6 @@ class UploadService {
|
|||||||
|
|
||||||
return buildUploadTask(
|
return buildUploadTask(
|
||||||
file,
|
file,
|
||||||
createdAt: asset.createdAt,
|
|
||||||
modifiedAt: asset.updatedAt,
|
|
||||||
originalFileName: asset.name,
|
originalFileName: asset.name,
|
||||||
deviceAssetId: asset.id,
|
deviceAssetId: asset.id,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
@@ -338,8 +334,6 @@ class UploadService {
|
|||||||
Future<UploadTask> buildUploadTask(
|
Future<UploadTask> buildUploadTask(
|
||||||
File file, {
|
File file, {
|
||||||
required String group,
|
required String group,
|
||||||
required DateTime createdAt,
|
|
||||||
required DateTime modifiedAt,
|
|
||||||
Map<String, String>? fields,
|
Map<String, String>? fields,
|
||||||
String? originalFileName,
|
String? originalFileName,
|
||||||
String? deviceAssetId,
|
String? deviceAssetId,
|
||||||
@@ -353,12 +347,15 @@ class UploadService {
|
|||||||
final headers = ApiService.getRequestHeaders();
|
final headers = ApiService.getRequestHeaders();
|
||||||
final deviceId = Store.get(StoreKey.deviceId);
|
final deviceId = Store.get(StoreKey.deviceId);
|
||||||
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
|
final (baseDirectory, directory, filename) = await Task.split(filePath: file.path);
|
||||||
|
final stats = await file.stat();
|
||||||
|
final fileCreatedAt = stats.changed;
|
||||||
|
final fileModifiedAt = stats.modified;
|
||||||
final fieldsMap = {
|
final fieldsMap = {
|
||||||
'filename': originalFileName ?? filename,
|
'filename': originalFileName ?? filename,
|
||||||
'deviceAssetId': deviceAssetId ?? '',
|
'deviceAssetId': deviceAssetId ?? '',
|
||||||
'deviceId': deviceId,
|
'deviceId': deviceId,
|
||||||
'fileCreatedAt': createdAt.toUtc().toIso8601String(),
|
'fileCreatedAt': fileCreatedAt.toUtc().toIso8601String(),
|
||||||
'fileModifiedAt': modifiedAt.toUtc().toIso8601String(),
|
'fileModifiedAt': fileModifiedAt.toUtc().toIso8601String(),
|
||||||
'isFavorite': isFavorite?.toString() ?? 'false',
|
'isFavorite': isFavorite?.toString() ?? 'false',
|
||||||
'duration': '0',
|
'duration': '0',
|
||||||
if (fields != null) ...fields,
|
if (fields != null) ...fields,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/local_album.entity.drift.dart';
|
||||||
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.dart';
|
||||||
|
|
||||||
|
extension LocalAlbumEntityDataHelper on LocalAlbumEntityData {
|
||||||
|
LocalAlbum toDto({int assetCount = 0}) {
|
||||||
|
return LocalAlbum(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
assetCount: assetCount,
|
||||||
|
backupSelection: backupSelection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocalAssetEntityDataHelper on LocalAssetEntityData {
|
||||||
|
LocalAsset toDto() {
|
||||||
|
return LocalAsset(
|
||||||
|
id: id,
|
||||||
|
name: name,
|
||||||
|
checksum: checksum,
|
||||||
|
type: type,
|
||||||
|
createdAt: createdAt,
|
||||||
|
updatedAt: updatedAt,
|
||||||
|
durationInSeconds: durationInSeconds,
|
||||||
|
isFavorite: isFavorite,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,10 +23,8 @@ import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.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';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
@@ -270,17 +268,11 @@ Future<List<void>> runNewSync(WidgetRef ref, {bool full = false}) {
|
|||||||
ref.read(backupProvider.notifier).cancelBackup();
|
ref.read(backupProvider.notifier).cancelBackup();
|
||||||
|
|
||||||
final backgroundManager = ref.read(backgroundSyncProvider);
|
final backgroundManager = ref.read(backgroundSyncProvider);
|
||||||
final isAlbumLinkedSyncEnable = ref.read(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
|
||||||
|
|
||||||
return Future.wait([
|
return Future.wait([
|
||||||
backgroundManager.syncLocal(full: full).then((_) {
|
backgroundManager.syncLocal(full: full).then((_) {
|
||||||
Logger("runNewSync").fine("Hashing assets after syncLocal");
|
Logger("runNewSync").fine("Hashing assets after syncLocal");
|
||||||
return backgroundManager.hashAssets();
|
return backgroundManager.hashAssets();
|
||||||
}),
|
}),
|
||||||
backgroundManager.syncRemote().then((_) {
|
backgroundManager.syncRemote(),
|
||||||
if (isAlbumLinkedSyncEnable) {
|
|
||||||
return backgroundManager.syncLinkedAlbum();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,12 @@ import 'package:fluttertoast/fluttertoast.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
class DriftAlbumInfoListTile extends HookConsumerWidget {
|
class DriftAlbumInfoListTile extends HookConsumerWidget {
|
||||||
@@ -19,6 +22,8 @@ class DriftAlbumInfoListTile extends HookConsumerWidget {
|
|||||||
final bool isSelected = album.backupSelection == BackupSelection.selected;
|
final bool isSelected = album.backupSelection == BackupSelection.selected;
|
||||||
final bool isExcluded = album.backupSelection == BackupSelection.excluded;
|
final bool isExcluded = album.backupSelection == BackupSelection.excluded;
|
||||||
|
|
||||||
|
final syncAlbum = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.syncAlbums);
|
||||||
|
|
||||||
buildTileColor() {
|
buildTileColor() {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25);
|
return context.isDarkTheme ? context.primaryColor.withAlpha(100) : context.primaryColor.withAlpha(25);
|
||||||
@@ -70,6 +75,9 @@ class DriftAlbumInfoListTile extends HookConsumerWidget {
|
|||||||
ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
|
ref.read(backupAlbumProvider.notifier).deselectAlbum(album);
|
||||||
} else {
|
} else {
|
||||||
ref.read(backupAlbumProvider.notifier).selectAlbum(album);
|
ref.read(backupAlbumProvider.notifier).selectAlbum(album);
|
||||||
|
if (syncAlbum) {
|
||||||
|
ref.read(albumProvider.notifier).createSyncAlbum(album.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
leading: buildIcon(),
|
leading: buildIcon(),
|
||||||
|
|||||||
@@ -1,153 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/album/local_album.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';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/domain/services/sync_linked_album.service.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart';
|
||||||
|
|
||||||
class DriftBackupSettings extends ConsumerWidget {
|
class DriftBackupSettings extends StatelessWidget {
|
||||||
const DriftBackupSettings({super.key});
|
const DriftBackupSettings({super.key});
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
|
||||||
return const SettingsSubPageScaffold(
|
|
||||||
settings: [
|
|
||||||
_UseWifiForUploadVideosButton(),
|
|
||||||
_UseWifiForUploadPhotosButton(),
|
|
||||||
Divider(indent: 16, endIndent: 16),
|
|
||||||
_AlbumSyncActionButton(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AlbumSyncActionButton extends ConsumerStatefulWidget {
|
|
||||||
const _AlbumSyncActionButton();
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<_AlbumSyncActionButton> createState() => _AlbumSyncActionButtonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton> {
|
|
||||||
bool isAlbumSyncInProgress = false;
|
|
||||||
|
|
||||||
Future<void> _manualSyncAlbums() async {
|
|
||||||
setState(() {
|
|
||||||
isAlbumSyncInProgress = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
await ref.read(backgroundSyncProvider).syncLinkedAlbum();
|
|
||||||
await ref.read(backgroundSyncProvider).syncRemote();
|
|
||||||
} catch (_) {
|
|
||||||
} finally {
|
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
|
||||||
setState(() {
|
|
||||||
isAlbumSyncInProgress = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _manageLinkedAlbums() async {
|
|
||||||
final currentUser = ref.read(currentUserProvider);
|
|
||||||
if (currentUser == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final localAlbums = ref.read(backupAlbumProvider);
|
|
||||||
final selectedBackupAlbums = localAlbums
|
|
||||||
.where((album) => album.backupSelection == BackupSelection.selected)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
await ref.read(syncLinkedAlbumServiceProvider).manageLinkedAlbums(selectedBackupAlbums, currentUser.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView(
|
return const SettingsSubPageScaffold(settings: [_UseWifiForUploadVideosButton(), _UseWifiForUploadPhotosButton()]);
|
||||||
shrinkWrap: true,
|
|
||||||
children: [
|
|
||||||
StreamBuilder(
|
|
||||||
stream: Store.watch(StoreKey.syncAlbums),
|
|
||||||
initialData: Store.tryGet(StoreKey.syncAlbums) ?? false,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final albumSyncEnable = snapshot.data ?? false;
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
"sync_albums".t(context: context),
|
|
||||||
style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
"sync_upload_album_setting_subtitle".t(context: context),
|
|
||||||
style: context.textTheme.labelLarge,
|
|
||||||
),
|
|
||||||
trailing: Switch(
|
|
||||||
value: albumSyncEnable,
|
|
||||||
onChanged: (bool newValue) async {
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue);
|
|
||||||
|
|
||||||
if (newValue == true) {
|
|
||||||
await _manageLinkedAlbums();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
AnimatedSize(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
child: AnimatedOpacity(
|
|
||||||
duration: const Duration(milliseconds: 200),
|
|
||||||
opacity: albumSyncEnable ? 1.0 : 0.0,
|
|
||||||
child: albumSyncEnable
|
|
||||||
? ListTile(
|
|
||||||
onTap: _manualSyncAlbums,
|
|
||||||
contentPadding: const EdgeInsets.only(left: 32, right: 16),
|
|
||||||
title: Text(
|
|
||||||
"organize_into_albums".t(context: context),
|
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
|
||||||
color: context.colorScheme.onSurface,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
subtitle: Text(
|
|
||||||
"organize_into_albums_description".t(context: context),
|
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
|
||||||
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
trailing: isAlbumSyncInProgress
|
|
||||||
? const SizedBox(
|
|
||||||
width: 32,
|
|
||||||
height: 32,
|
|
||||||
child: CircularProgressIndicator.adaptive(strokeWidth: 2),
|
|
||||||
)
|
|
||||||
: IconButton(
|
|
||||||
onPressed: _manualSyncAlbums,
|
|
||||||
icon: const Icon(Icons.sync_rounded),
|
|
||||||
color: context.colorScheme.onSurface.withValues(alpha: 0.7),
|
|
||||||
iconSize: 20,
|
|
||||||
constraints: const BoxConstraints(minWidth: 32, minHeight: 32),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -109,37 +109,6 @@ class BetaSyncSettings extends HookConsumerWidget {
|
|||||||
await ref.read(storageRepositoryProvider).clearCache();
|
await ref.read(storageRepositoryProvider).clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> resetSqliteDb(BuildContext context, Future<void> Function() resetDatabase) {
|
|
||||||
return showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: Text("reset_sqlite".t(context: context)),
|
|
||||||
content: Text("reset_sqlite_confirmation".t(context: context)),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => context.pop(),
|
|
||||||
child: Text("cancel".t(context: context)),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await resetDatabase();
|
|
||||||
context.pop();
|
|
||||||
context.scaffoldMessenger.showSnackBar(
|
|
||||||
SnackBar(content: Text("reset_sqlite_success".t(context: context))),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
"confirm".t(context: context),
|
|
||||||
style: TextStyle(color: context.colorScheme.error),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FutureBuilder<List<dynamic>>(
|
return FutureBuilder<List<dynamic>>(
|
||||||
future: loadCounts(),
|
future: loadCounts(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@@ -147,33 +116,6 @@ class BetaSyncSettings extends HookConsumerWidget {
|
|||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
|
||||||
"Error occur, reset the local database by tapping the button below",
|
|
||||||
style: context.textTheme.bodyLarge,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
ListTile(
|
|
||||||
title: Text(
|
|
||||||
"reset_sqlite".t(context: context),
|
|
||||||
style: TextStyle(color: context.colorScheme.error, fontWeight: FontWeight.w500),
|
|
||||||
),
|
|
||||||
leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error),
|
|
||||||
onTap: () async {
|
|
||||||
await resetSqliteDb(context, resetDatabase);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final assetCounts = snapshot.data![0]! as (int, int);
|
final assetCounts = snapshot.data![0]! as (int, int);
|
||||||
final localAssetCount = assetCounts.$1;
|
final localAssetCount = assetCounts.$1;
|
||||||
final remoteAssetCount = assetCounts.$2;
|
final remoteAssetCount = assetCounts.$2;
|
||||||
@@ -328,7 +270,34 @@ class BetaSyncSettings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error),
|
leading: Icon(Icons.settings_backup_restore_rounded, color: context.colorScheme.error),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await resetSqliteDb(context, resetDatabase);
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("reset_sqlite".t(context: context)),
|
||||||
|
content: Text("reset_sqlite_confirmation".t(context: context)),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => context.pop(),
|
||||||
|
child: Text("cancel".t(context: context)),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await resetDatabase();
|
||||||
|
context.pop();
|
||||||
|
context.scaffoldMessenger.showSnackBar(
|
||||||
|
SnackBar(content: Text("reset_sqlite_success".t(context: context))),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
"confirm".t(context: context),
|
||||||
|
style: TextStyle(color: context.colorScheme.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
Generated
+1
-1
@@ -3,7 +3,7 @@ Immich API
|
|||||||
|
|
||||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||||
|
|
||||||
- API version: 1.141.0
|
- API version: 1.140.1
|
||||||
- Generator version: 7.8.0
|
- Generator version: 7.8.0
|
||||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ name: immich_mobile
|
|||||||
description: Immich - selfhosted backup media file on mobile phone
|
description: Immich - selfhosted backup media file on mobile phone
|
||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.141.0+3012
|
version: 1.140.1+3011
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.8.0 <4.0.0'
|
sdk: '>=3.8.0 <4.0.0'
|
||||||
|
|||||||
+1
-4
@@ -11,7 +11,6 @@ import 'schema_v5.dart' as v5;
|
|||||||
import 'schema_v6.dart' as v6;
|
import 'schema_v6.dart' as v6;
|
||||||
import 'schema_v7.dart' as v7;
|
import 'schema_v7.dart' as v7;
|
||||||
import 'schema_v8.dart' as v8;
|
import 'schema_v8.dart' as v8;
|
||||||
import 'schema_v9.dart' as v9;
|
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
class GeneratedHelper implements SchemaInstantiationHelper {
|
||||||
@override
|
@override
|
||||||
@@ -33,12 +32,10 @@ class GeneratedHelper implements SchemaInstantiationHelper {
|
|||||||
return v7.DatabaseAtV7(db);
|
return v7.DatabaseAtV7(db);
|
||||||
case 8:
|
case 8:
|
||||||
return v8.DatabaseAtV8(db);
|
return v8.DatabaseAtV8(db);
|
||||||
case 9:
|
|
||||||
return v9.DatabaseAtV9(db);
|
|
||||||
default:
|
default:
|
||||||
throw MissingSchemaException(version, versions);
|
throw MissingSchemaException(version, versions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
static const versions = const [1, 2, 3, 4, 5, 6, 7, 8];
|
||||||
}
|
}
|
||||||
|
|||||||
-6712
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ function dart {
|
|||||||
patch --no-backup-if-mismatch -u api.mustache <api.mustache.patch
|
patch --no-backup-if-mismatch -u api.mustache <api.mustache.patch
|
||||||
|
|
||||||
cd ../../
|
cd ../../
|
||||||
pnpx @openapitools/openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile/openapi -t ./templates/mobile
|
pnpm dlx @openapitools/openapi-generator-cli generate -g dart -i ./immich-openapi-specs.json -o ../mobile/openapi -t ./templates/mobile
|
||||||
|
|
||||||
# Post generate patches
|
# Post generate patches
|
||||||
patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api_client.dart <./patch/api_client.dart.patch
|
patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api_client.dart <./patch/api_client.dart.patch
|
||||||
@@ -27,7 +27,7 @@ function dart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function typescript {
|
function typescript {
|
||||||
pnpx oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/src/fetch-client.ts
|
pnpm dlx oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/src/fetch-client.ts
|
||||||
pnpm --filter @immich/sdk install --frozen-lockfile
|
pnpm --filter @immich/sdk install --frozen-lockfile
|
||||||
pnpm --filter @immich/sdk build
|
pnpm --filter @immich/sdk build
|
||||||
}
|
}
|
||||||
@@ -35,8 +35,8 @@ function typescript {
|
|||||||
# requires server to be built
|
# requires server to be built
|
||||||
(
|
(
|
||||||
cd ..
|
cd ..
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich build
|
SHARP_IGNORE_GLOBAL_LIBVIPS=true mise run server:build
|
||||||
pnpm --filter immich sync:open-api
|
mise run server:open-api
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ $1 == 'dart' ]]; then
|
if [[ $1 == 'dart' ]]; then
|
||||||
|
|||||||
@@ -9790,7 +9790,7 @@
|
|||||||
"info": {
|
"info": {
|
||||||
"title": "Immich",
|
"title": "Immich",
|
||||||
"description": "Immich API",
|
"description": "Immich API",
|
||||||
"version": "1.141.0",
|
"version": "1.140.1",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
},
|
},
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@immich/sdk",
|
"name": "@immich/sdk",
|
||||||
"version": "1.141.0",
|
"version": "1.140.1",
|
||||||
"description": "Auto-generated TypeScript SDK for the Immich API",
|
"description": "Auto-generated TypeScript SDK for the Immich API",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./build/index.js",
|
"main": "./build/index.js",
|
||||||
@@ -11,23 +11,17 @@
|
|||||||
"default": "./build/index.js"
|
"default": "./build/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
|
||||||
"build": "tsc"
|
|
||||||
},
|
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@oazapfts/runtime": "^1.0.2"
|
"@oazapfts/runtime": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.18.0",
|
"@types/node": "^22.17.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/immich-app/immich.git",
|
"url": "git+https://github.com/immich-app/immich.git",
|
||||||
"directory": "open-api/typescript-sdk"
|
"directory": "open-api/typescript-sdk"
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Immich
|
* Immich
|
||||||
* 1.141.0
|
* 1.140.1
|
||||||
* DO NOT MODIFY - This file has been generated using oazapfts.
|
* DO NOT MODIFY - This file has been generated using oazapfts.
|
||||||
* See https://www.npmjs.com/package/oazapfts
|
* See https://www.npmjs.com/package/oazapfts
|
||||||
*/
|
*/
|
||||||
|
|||||||
Generated
+1112
-553
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -27,7 +27,7 @@ onlyBuiltDependencies:
|
|||||||
- '@tailwindcss/oxide'
|
- '@tailwindcss/oxide'
|
||||||
overrides:
|
overrides:
|
||||||
canvas: 2.11.2
|
canvas: 2.11.2
|
||||||
sharp: ^0.34.3
|
sharp: ^0.34.2
|
||||||
packageExtensions:
|
packageExtensions:
|
||||||
nestjs-kysely:
|
nestjs-kysely:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
+4
-35
@@ -1,38 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "immich",
|
"name": "immich",
|
||||||
"version": "1.141.0",
|
"version": "1.140.1",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "",
|
"author": "",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"scripts": {
|
|
||||||
"build": "nest build",
|
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write .",
|
|
||||||
"start": "npm run start:dev",
|
|
||||||
"nest": "nest",
|
|
||||||
"start:dev": "nest start --watch --",
|
|
||||||
"start:debug": "nest start --debug 0.0.0.0:9230 --watch --",
|
|
||||||
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --max-warnings 0",
|
|
||||||
"lint:fix": "npm run lint -- --fix",
|
|
||||||
"check": "tsc --noEmit",
|
|
||||||
"check:code": "npm run format && npm run lint && npm run check",
|
|
||||||
"check:all": "npm run check:code && npm run test:cov",
|
|
||||||
"test": "vitest --config test/vitest.config.mjs",
|
|
||||||
"test:cov": "vitest --config test/vitest.config.mjs --coverage",
|
|
||||||
"test:medium": "vitest --config test/vitest.config.medium.mjs",
|
|
||||||
"typeorm": "typeorm",
|
|
||||||
"lifecycle": "node ./dist/utils/lifecycle.js",
|
|
||||||
"migrations:debug": "node ./dist/bin/migrations.js debug",
|
|
||||||
"migrations:generate": "node ./dist/bin/migrations.js generate",
|
|
||||||
"migrations:create": "node ./dist/bin/migrations.js create",
|
|
||||||
"migrations:run": "node ./dist/bin/migrations.js run",
|
|
||||||
"schema:drop": "node ./dist/bin/migrations.js query 'DROP schema public cascade; CREATE schema public;'",
|
|
||||||
"schema:reset": "npm run schema:drop && npm run migrations:run",
|
|
||||||
"sync:open-api": "node ./dist/bin/sync-open-api.js",
|
|
||||||
"sync:sql": "node ./dist/bin/sync-sql.js",
|
|
||||||
"email:dev": "email dev -p 3050 --dir src/emails"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/bullmq": "^11.0.1",
|
"@nestjs/bullmq": "^11.0.1",
|
||||||
"@nestjs/common": "^11.0.4",
|
"@nestjs/common": "^11.0.4",
|
||||||
@@ -103,7 +75,7 @@
|
|||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "^2.14.0",
|
"sanitize-html": "^2.14.0",
|
||||||
"semver": "^7.6.2",
|
"semver": "^7.6.2",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.2",
|
||||||
"sirv": "^3.0.0",
|
"sirv": "^3.0.0",
|
||||||
"socket.io": "^4.8.1",
|
"socket.io": "^4.8.1",
|
||||||
"tailwindcss-preset-email": "^1.4.0",
|
"tailwindcss-preset-email": "^1.4.0",
|
||||||
@@ -135,7 +107,7 @@
|
|||||||
"@types/luxon": "^3.6.2",
|
"@types/luxon": "^3.6.2",
|
||||||
"@types/mock-fs": "^4.13.1",
|
"@types/mock-fs": "^4.13.1",
|
||||||
"@types/multer": "^2.0.0",
|
"@types/multer": "^2.0.0",
|
||||||
"@types/node": "^22.18.0",
|
"@types/node": "^22.13.14",
|
||||||
"@types/nodemailer": "^6.4.14",
|
"@types/nodemailer": "^6.4.14",
|
||||||
"@types/picomatch": "^4.0.0",
|
"@types/picomatch": "^4.0.0",
|
||||||
"@types/pngjs": "^6.0.5",
|
"@types/pngjs": "^6.0.5",
|
||||||
@@ -172,10 +144,7 @@
|
|||||||
"vite-tsconfig-paths": "^5.0.0",
|
"vite-tsconfig-paths": "^5.0.0",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
},
|
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"sharp": "^0.34.3"
|
"sharp": "^0.34.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { AssetMediaController } from 'src/controllers/asset-media.controller';
|
import { AssetMediaController } from 'src/controllers/asset-media.controller';
|
||||||
import { AssetMediaStatus } from 'src/dtos/asset-media-response.dto';
|
|
||||||
import { AssetMetadataKey } from 'src/enum';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { AssetMediaService } from 'src/services/asset-media.service';
|
import { AssetMediaService } from 'src/services/asset-media.service';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
@@ -13,7 +11,7 @@ const makeUploadDto = (options?: { omit: string }): Record<string, any> => {
|
|||||||
deviceId: 'TEST',
|
deviceId: 'TEST',
|
||||||
fileCreatedAt: new Date().toISOString(),
|
fileCreatedAt: new Date().toISOString(),
|
||||||
fileModifiedAt: new Date().toISOString(),
|
fileModifiedAt: new Date().toISOString(),
|
||||||
isFavorite: 'false',
|
isFavorite: 'testing',
|
||||||
duration: '0:00:00.000000',
|
duration: '0:00:00.000000',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -29,20 +27,16 @@ describe(AssetMediaController.name, () => {
|
|||||||
let ctx: ControllerContext;
|
let ctx: ControllerContext;
|
||||||
const assetData = Buffer.from('123');
|
const assetData = Buffer.from('123');
|
||||||
const filename = 'example.png';
|
const filename = 'example.png';
|
||||||
const service = mockBaseService(AssetMediaService);
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
ctx = await controllerSetup(AssetMediaController, [
|
ctx = await controllerSetup(AssetMediaController, [
|
||||||
{ provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) },
|
{ provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) },
|
||||||
{ provide: AssetMediaService, useValue: service },
|
{ provide: AssetMediaService, useValue: mockBaseService(AssetMediaService) },
|
||||||
]);
|
]);
|
||||||
return () => ctx.close();
|
return () => ctx.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service.resetAllMocks();
|
|
||||||
service.uploadAsset.mockResolvedValue({ status: AssetMediaStatus.DUPLICATE, id: factory.uuid() });
|
|
||||||
|
|
||||||
ctx.reset();
|
ctx.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,61 +46,13 @@ describe(AssetMediaController.name, () => {
|
|||||||
expect(ctx.authenticate).toHaveBeenCalled();
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should accept metadata', async () => {
|
|
||||||
const mobileMetadata = { key: AssetMetadataKey.MobileApp, value: { iCloudId: '123' } };
|
|
||||||
const { status } = await request(ctx.getHttpServer())
|
|
||||||
.post('/assets')
|
|
||||||
.attach('assetData', assetData, filename)
|
|
||||||
.field({
|
|
||||||
...makeUploadDto(),
|
|
||||||
metadata: JSON.stringify([mobileMetadata]),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(service.uploadAsset).toHaveBeenCalledWith(
|
|
||||||
undefined,
|
|
||||||
expect.objectContaining({ metadata: [mobileMetadata] }),
|
|
||||||
expect.objectContaining({ originalName: 'example.png' }),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(status).toBe(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should handle invalid metadata json', async () => {
|
|
||||||
const { status, body } = await request(ctx.getHttpServer())
|
|
||||||
.post('/assets')
|
|
||||||
.attach('assetData', assetData, filename)
|
|
||||||
.field({
|
|
||||||
...makeUploadDto(),
|
|
||||||
metadata: 'not-a-string-string',
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(factory.responses.badRequest(['metadata must be valid JSON']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should validate iCloudId is a string', async () => {
|
|
||||||
const { status, body } = await request(ctx.getHttpServer())
|
|
||||||
.post('/assets')
|
|
||||||
.attach('assetData', assetData, filename)
|
|
||||||
.field({
|
|
||||||
...makeUploadDto(),
|
|
||||||
metadata: JSON.stringify([{ key: AssetMetadataKey.MobileApp, value: { iCloudId: 123 } }]),
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(status).toBe(400);
|
|
||||||
expect(body).toEqual(factory.responses.badRequest(['metadata.0.value.iCloudId must be a string']));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should require `deviceAssetId`', async () => {
|
it('should require `deviceAssetId`', async () => {
|
||||||
const { status, body } = await request(ctx.getHttpServer())
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
.post('/assets')
|
.post('/assets')
|
||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field({ ...makeUploadDto({ omit: 'deviceAssetId' }) });
|
.field({ ...makeUploadDto({ omit: 'deviceAssetId' }) });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
factory.responses.badRequest(['deviceAssetId must be a string', 'deviceAssetId should not be empty']),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require `deviceId`', async () => {
|
it('should require `deviceId`', async () => {
|
||||||
@@ -115,7 +61,7 @@ describe(AssetMediaController.name, () => {
|
|||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field({ ...makeUploadDto({ omit: 'deviceId' }) });
|
.field({ ...makeUploadDto({ omit: 'deviceId' }) });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(factory.responses.badRequest(['deviceId must be a string', 'deviceId should not be empty']));
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require `fileCreatedAt`', async () => {
|
it('should require `fileCreatedAt`', async () => {
|
||||||
@@ -124,20 +70,25 @@ describe(AssetMediaController.name, () => {
|
|||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field({ ...makeUploadDto({ omit: 'fileCreatedAt' }) });
|
.field({ ...makeUploadDto({ omit: 'fileCreatedAt' }) });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
factory.responses.badRequest(['fileCreatedAt must be a Date instance', 'fileCreatedAt should not be empty']),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should require `fileModifiedAt`', async () => {
|
it('should require `fileModifiedAt`', async () => {
|
||||||
const { status, body } = await request(ctx.getHttpServer())
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
.post('/assets')
|
.post('/assets')
|
||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field(makeUploadDto({ omit: 'fileModifiedAt' }));
|
.field({ ...makeUploadDto({ omit: 'fileModifiedAt' }) });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
factory.responses.badRequest(['fileModifiedAt must be a Date instance', 'fileModifiedAt should not be empty']),
|
});
|
||||||
);
|
|
||||||
|
it('should require `duration`', async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.post('/assets')
|
||||||
|
.attach('assetData', assetData, filename)
|
||||||
|
.field({ ...makeUploadDto({ omit: 'duration' }) });
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if `isFavorite` is not a boolean', async () => {
|
it('should throw if `isFavorite` is not a boolean', async () => {
|
||||||
@@ -146,18 +97,16 @@ describe(AssetMediaController.name, () => {
|
|||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field({ ...makeUploadDto(), isFavorite: 'not-a-boolean' });
|
.field({ ...makeUploadDto(), isFavorite: 'not-a-boolean' });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(factory.responses.badRequest(['isFavorite must be a boolean value']));
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if `visibility` is not an enum', async () => {
|
it('should throw if `visibility` is not an enum', async () => {
|
||||||
const { status, body } = await request(ctx.getHttpServer())
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
.post('/assets')
|
.post('/assets')
|
||||||
.attach('assetData', assetData, filename)
|
.attach('assetData', assetData, filename)
|
||||||
.field({ ...makeUploadDto(), visibility: 'not-an-option' });
|
.field({ ...makeUploadDto(), visibility: 'not-a-boolean' });
|
||||||
expect(status).toBe(400);
|
expect(status).toBe(400);
|
||||||
expect(body).toEqual(
|
expect(body).toEqual(factory.responses.badRequest());
|
||||||
factory.responses.badRequest([expect.stringContaining('visibility must be one of the following values:')]),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO figure out how to deal with `sendFile`
|
// TODO figure out how to deal with `sendFile`
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { plainToInstance, Transform, Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { ArrayNotEmpty, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
import { ArrayNotEmpty, IsArray, IsNotEmpty, IsString, ValidateNested } from 'class-validator';
|
||||||
import { AssetMetadataUpsertItemDto } from 'src/dtos/asset.dto';
|
import { AssetMetadataUpsertItemDto } from 'src/dtos/asset.dto';
|
||||||
import { AssetVisibility } from 'src/enum';
|
import { AssetVisibility } from 'src/enum';
|
||||||
@@ -66,18 +65,10 @@ export class AssetMediaCreateDto extends AssetMediaBase {
|
|||||||
@ValidateUUID({ optional: true })
|
@ValidateUUID({ optional: true })
|
||||||
livePhotoVideoId?: string;
|
livePhotoVideoId?: string;
|
||||||
|
|
||||||
@Transform(({ value }) => {
|
|
||||||
try {
|
|
||||||
const json = JSON.parse(value);
|
|
||||||
const items = Array.isArray(json) ? json : [json];
|
|
||||||
return items.map((item) => plainToInstance(AssetMetadataUpsertItemDto, item));
|
|
||||||
} catch {
|
|
||||||
throw new BadRequestException(['metadata must be valid JSON']);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@Optional()
|
@Optional()
|
||||||
@ValidateNested({ each: true })
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
@Type(() => AssetMetadataUpsertItemDto)
|
||||||
metadata!: AssetMetadataUpsertItemDto[];
|
metadata!: AssetMetadataUpsertItemDto[];
|
||||||
|
|
||||||
@ApiProperty({ type: 'string', format: 'binary', required: false })
|
@ApiProperty({ type: 'string', format: 'binary', required: false })
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ export class MediaRepository {
|
|||||||
failOn: options.processInvalidImages ? 'none' : 'error',
|
failOn: options.processInvalidImages ? 'none' : 'error',
|
||||||
limitInputPixels: false,
|
limitInputPixels: false,
|
||||||
raw: options.raw,
|
raw: options.raw,
|
||||||
unlimited: true,
|
|
||||||
})
|
})
|
||||||
.pipelineColorspace(options.colorspace === Colorspace.Srgb ? 'srgb' : 'rgb16')
|
.pipelineColorspace(options.colorspace === Colorspace.Srgb ? 'srgb' : 'rgb16')
|
||||||
.withIccProfile(options.colorspace);
|
.withIccProfile(options.colorspace);
|
||||||
|
|||||||
@@ -835,11 +835,7 @@ export class MetadataService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDates(
|
private getDates(asset: { id: string; originalPath: string }, exifTags: ImmichTags, stats: Stats) {
|
||||||
asset: { id: string; originalPath: string; fileCreatedAt: Date },
|
|
||||||
exifTags: ImmichTags,
|
|
||||||
stats: Stats,
|
|
||||||
) {
|
|
||||||
const result = firstDateTime(exifTags);
|
const result = firstDateTime(exifTags);
|
||||||
const tag = result?.tag;
|
const tag = result?.tag;
|
||||||
const dateTime = result?.dateTime;
|
const dateTime = result?.dateTime;
|
||||||
@@ -868,12 +864,7 @@ export class MetadataService extends BaseService {
|
|||||||
if (!localDateTime || !dateTimeOriginal) {
|
if (!localDateTime || !dateTimeOriginal) {
|
||||||
// FileCreateDate is not available on linux, likely because exiftool hasn't integrated the statx syscall yet
|
// FileCreateDate is not available on linux, likely because exiftool hasn't integrated the statx syscall yet
|
||||||
// birthtime is not available in Docker on macOS, so it appears as 0
|
// birthtime is not available in Docker on macOS, so it appears as 0
|
||||||
const earliestDate = new Date(
|
const earliestDate = stats.birthtimeMs ? new Date(Math.min(stats.mtimeMs, stats.birthtimeMs)) : stats.mtime;
|
||||||
Math.min(
|
|
||||||
asset.fileCreatedAt.getTime(),
|
|
||||||
stats.birthtimeMs ? Math.min(stats.mtimeMs, stats.birthtimeMs) : stats.mtime.getTime(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`No exif date time found, falling back on ${earliestDate.toISOString()}, earliest of file creation and modification for asset ${asset.id}: ${asset.originalPath}`,
|
`No exif date time found, falling back on ${earliestDate.toISOString()}, earliest of file creation and modification for asset ${asset.id}: ${asset.originalPath}`,
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-24
@@ -1,16 +1,12 @@
|
|||||||
import { CallHandler, ExecutionContext, Provider, ValidationPipe } from '@nestjs/common';
|
import { CallHandler, Provider, ValidationPipe } from '@nestjs/common';
|
||||||
import { APP_GUARD, APP_PIPE } from '@nestjs/core';
|
import { APP_GUARD, APP_PIPE } from '@nestjs/core';
|
||||||
import { transformException } from '@nestjs/platform-express/multer/multer/multer.utils';
|
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
import { ClassConstructor } from 'class-transformer';
|
import { ClassConstructor } from 'class-transformer';
|
||||||
import { NextFunction } from 'express';
|
|
||||||
import { Kysely } from 'kysely';
|
import { Kysely } from 'kysely';
|
||||||
import multer from 'multer';
|
|
||||||
import { ChildProcessWithoutNullStreams } from 'node:child_process';
|
import { ChildProcessWithoutNullStreams } from 'node:child_process';
|
||||||
import { Readable, Writable } from 'node:stream';
|
import { Readable, Writable } from 'node:stream';
|
||||||
import { PNG } from 'pngjs';
|
import { PNG } from 'pngjs';
|
||||||
import postgres from 'postgres';
|
import postgres from 'postgres';
|
||||||
import { UploadFieldName } from 'src/dtos/asset-media.dto';
|
|
||||||
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
|
import { AssetUploadInterceptor } from 'src/middleware/asset-upload.interceptor';
|
||||||
import { AuthGuard } from 'src/middleware/auth.guard';
|
import { AuthGuard } from 'src/middleware/auth.guard';
|
||||||
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
import { FileUploadInterceptor } from 'src/middleware/file-upload.interceptor';
|
||||||
@@ -86,24 +82,6 @@ export type ControllerContext = {
|
|||||||
|
|
||||||
export const controllerSetup = async (controller: ClassConstructor<unknown>, providers: Provider[]) => {
|
export const controllerSetup = async (controller: ClassConstructor<unknown>, providers: Provider[]) => {
|
||||||
const noopInterceptor = { intercept: (ctx: never, next: CallHandler<unknown>) => next.handle() };
|
const noopInterceptor = { intercept: (ctx: never, next: CallHandler<unknown>) => next.handle() };
|
||||||
const upload = multer({ storage: multer.memoryStorage() });
|
|
||||||
const memoryFileInterceptor = {
|
|
||||||
intercept: async (ctx: ExecutionContext, next: CallHandler<unknown>) => {
|
|
||||||
const context = ctx.switchToHttp();
|
|
||||||
const handler = upload.fields([
|
|
||||||
{ name: UploadFieldName.ASSET_DATA, maxCount: 1 },
|
|
||||||
{ name: UploadFieldName.SIDECAR_DATA, maxCount: 1 },
|
|
||||||
]);
|
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
|
||||||
const next: NextFunction = (error) => (error ? reject(transformException(error)) : resolve());
|
|
||||||
const maybePromise = handler(context.getRequest(), context.getResponse(), next);
|
|
||||||
Promise.resolve(maybePromise).catch((error) => reject(error));
|
|
||||||
});
|
|
||||||
|
|
||||||
return next.handle();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const moduleRef = await Test.createTestingModule({
|
const moduleRef = await Test.createTestingModule({
|
||||||
controllers: [controller],
|
controllers: [controller],
|
||||||
providers: [
|
providers: [
|
||||||
@@ -115,7 +93,7 @@ export const controllerSetup = async (controller: ClassConstructor<unknown>, pro
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
.overrideInterceptor(FileUploadInterceptor)
|
.overrideInterceptor(FileUploadInterceptor)
|
||||||
.useValue(memoryFileInterceptor)
|
.useValue(noopInterceptor)
|
||||||
.overrideInterceptor(AssetUploadInterceptor)
|
.overrideInterceptor(AssetUploadInterceptor)
|
||||||
.useValue(noopInterceptor)
|
.useValue(noopInterceptor)
|
||||||
.compile();
|
.compile();
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
22.18.0
|
|
||||||
+1
-26
@@ -1,30 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "immich-web",
|
"name": "immich-web",
|
||||||
"version": "1.141.0",
|
"version": "1.140.1",
|
||||||
"license": "GNU Affero General Public License version 3",
|
"license": "GNU Affero General Public License version 3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
|
||||||
"dev": "vite dev --host 0.0.0.0 --port 3000",
|
|
||||||
"build": "vite build",
|
|
||||||
"build:stats": "BUILD_STATS=true vite build",
|
|
||||||
"package": "svelte-kit package",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"check:svelte": "svelte-check --no-tsconfig --fail-on-warnings --compiler-warnings 'reactive_declaration_non_reactive_property:ignore' --ignore src/lib/components/photos-page/asset-grid.svelte",
|
|
||||||
"check:typescript": "tsc --noEmit",
|
|
||||||
"check:watch": "npm run check:svelte -- --watch",
|
|
||||||
"check:code": "npm run format && npm run lint:p && npm run check:svelte && npm run check:typescript",
|
|
||||||
"check:all": "npm run check:code && npm run test:cov",
|
|
||||||
"lint": "eslint . --max-warnings 0",
|
|
||||||
"lint:p": "eslint-p . --max-warnings 0 --concurrency=4",
|
|
||||||
"lint:fix": "npm run lint -- --fix",
|
|
||||||
"format": "prettier --check .",
|
|
||||||
"format:fix": "prettier --write . && npm run format:i18n",
|
|
||||||
"format:i18n": "pnpx sort-json ../i18n/*.json",
|
|
||||||
"test": "vitest --run",
|
|
||||||
"test:cov": "vitest --coverage",
|
|
||||||
"test:watch": "vitest dev",
|
|
||||||
"prepare": "svelte-kit sync"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
"@formatjs/icu-messageformat-parser": "^2.9.8",
|
||||||
"@immich/sdk": "file:../open-api/typescript-sdk",
|
"@immich/sdk": "file:../open-api/typescript-sdk",
|
||||||
@@ -107,8 +85,5 @@
|
|||||||
"typescript-eslint": "^8.28.0",
|
"typescript-eslint": "^8.28.0",
|
||||||
"vite": "^7.1.2",
|
"vite": "^7.1.2",
|
||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
|
||||||
"volta": {
|
|
||||||
"node": "22.18.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user